/* * Copyright (C) 2009 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. */ #import "config.h" #if USE(ACCELERATED_COMPOSITING) #import "GraphicsLayerCA.h" #import "Animation.h" #import "BlockExceptions.h" #import "CString.h" #import "FloatConversion.h" #import "FloatRect.h" #import "Image.h" #import "PlatformString.h" #import #import "RotateTransformOperation.h" #import "ScaleTransformOperation.h" #import "SystemTime.h" #import "TranslateTransformOperation.h" #import "WebLayer.h" #import "WebTiledLayer.h" #import #import 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; // 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 cTiledLayerTileSize = 512; // 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 double mediaTimeToCurrentTime(CFTimeInterval t) { return WTF::currentTime() + t - CACurrentMediaTime(); } } // namespace WebCore static NSString* const WebAnimationCSSPropertyKey = @"GraphicsLayerCA_property"; @interface WebAnimationDelegate : NSObject { WebCore::GraphicsLayerCA* m_graphicsLayer; } - (void)animationDidStart:(CAAnimation *)anim; - (WebCore::GraphicsLayerCA*)graphicsLayer; - (void)setLayer:(WebCore::GraphicsLayerCA*)graphicsLayer; @end @implementation WebAnimationDelegate - (void)animationDidStart:(CAAnimation *)animation { if (!m_graphicsLayer) return; double startTime = WebCore::mediaTimeToCurrentTime([animation beginTime]); m_graphicsLayer->client()->notifyAnimationStarted(m_graphicsLayer, startTime); } - (WebCore::GraphicsLayerCA*)graphicsLayer { return m_graphicsLayer; } - (void)setLayer:(WebCore::GraphicsLayerCA*)graphicsLayer { m_graphicsLayer = graphicsLayer; } @end namespace WebCore { static inline void copyTransform(CATransform3D& toT3D, const TransformationMatrix& t) { toT3D.m11 = narrowPrecisionToFloat(t.m11()); toT3D.m12 = narrowPrecisionToFloat(t.m12()); toT3D.m13 = narrowPrecisionToFloat(t.m13()); toT3D.m14 = narrowPrecisionToFloat(t.m14()); toT3D.m21 = narrowPrecisionToFloat(t.m21()); toT3D.m22 = narrowPrecisionToFloat(t.m22()); toT3D.m23 = narrowPrecisionToFloat(t.m23()); toT3D.m24 = narrowPrecisionToFloat(t.m24()); toT3D.m31 = narrowPrecisionToFloat(t.m31()); toT3D.m32 = narrowPrecisionToFloat(t.m32()); toT3D.m33 = narrowPrecisionToFloat(t.m33()); toT3D.m34 = narrowPrecisionToFloat(t.m34()); toT3D.m41 = narrowPrecisionToFloat(t.m41()); toT3D.m42 = narrowPrecisionToFloat(t.m42()); toT3D.m43 = narrowPrecisionToFloat(t.m43()); toT3D.m44 = narrowPrecisionToFloat(t.m44()); } static NSValue* getTransformFunctionValue(const GraphicsLayer::TransformValue& transformValue, size_t index, const IntSize& size, TransformOperation::OperationType transformType) { TransformOperation* op = (index >= transformValue.value()->operations().size()) ? 0 : transformValue.value()->operations()[index].get(); switch (transformType) { case TransformOperation::ROTATE: case TransformOperation::ROTATE_X: case TransformOperation::ROTATE_Y: return [NSNumber numberWithDouble:op ? deg2rad(static_cast(op)->angle()) : 0]; case TransformOperation::SCALE_X: return [NSNumber numberWithDouble:op ? static_cast(op)->x() : 0]; case TransformOperation::SCALE_Y: return [NSNumber numberWithDouble:op ? static_cast(op)->y() : 0]; case TransformOperation::SCALE_Z: return [NSNumber numberWithDouble:op ? static_cast(op)->z() : 0]; case TransformOperation::TRANSLATE_X: return [NSNumber numberWithDouble:op ? static_cast(op)->x(size) : 0]; case TransformOperation::TRANSLATE_Y: return [NSNumber numberWithDouble:op ? static_cast(op)->y(size) : 0]; case TransformOperation::TRANSLATE_Z: return [NSNumber numberWithDouble:op ? static_cast(op)->z(size) : 0]; case TransformOperation::SCALE: case TransformOperation::TRANSLATE: case TransformOperation::SKEW_X: case TransformOperation::SKEW_Y: case TransformOperation::SKEW: case TransformOperation::MATRIX: case TransformOperation::SCALE_3D: case TransformOperation::TRANSLATE_3D: case TransformOperation::ROTATE_3D: case TransformOperation::MATRIX_3D: case TransformOperation::PERSPECTIVE: case TransformOperation::IDENTITY: case TransformOperation::NONE: { TransformationMatrix t; if (op) op->apply(t, size); CATransform3D cat; copyTransform(cat, t); return [NSValue valueWithCATransform3D:cat]; } } return 0; } #if HAVE_MODERN_QUARTZCORE static NSString* getValueFunctionNameForTransformOperation(TransformOperation::OperationType transformType) { // Use literal strings to avoid link-time dependency on those symbols. switch (transformType) { case TransformOperation::ROTATE_X: return @"rotateX"; // kCAValueFunctionRotateX; case TransformOperation::ROTATE_Y: return @"rotateY"; // kCAValueFunctionRotateY; case TransformOperation::ROTATE: return @"rotateZ"; // kCAValueFunctionRotateZ; case TransformOperation::SCALE_X: return @"scaleX"; // kCAValueFunctionScaleX; case TransformOperation::SCALE_Y: return @"scaleY"; // kCAValueFunctionScaleY; case TransformOperation::SCALE_Z: return @"scaleZ"; // kCAValueFunctionScaleZ; case TransformOperation::TRANSLATE_X: return @"translateX"; // kCAValueFunctionTranslateX; case TransformOperation::TRANSLATE_Y: return @"translateY"; // kCAValueFunctionTranslateY; case TransformOperation::TRANSLATE_Z: return @"translateZ"; // kCAValueFunctionTranslateZ; default: return nil; } } #endif static CAMediaTimingFunction* getCAMediaTimingFunction(const TimingFunction& timingFunction) { switch (timingFunction.type()) { case LinearTimingFunction: return [CAMediaTimingFunction functionWithName:@"linear"]; case CubicBezierTimingFunction: return [CAMediaTimingFunction functionWithControlPoints:static_cast(timingFunction.x1()) :static_cast(timingFunction.y1()) :static_cast(timingFunction.x2()) :static_cast(timingFunction.y2())]; } return 0; } #ifndef NDEBUG static void setLayerBorderColor(PlatformLayer* layer, const Color& color) { CGColorRef borderColor = createCGColor(color); [layer setBorderColor:borderColor]; CGColorRelease(borderColor); } static void clearBorderColor(PlatformLayer* layer) { [layer setBorderColor:nil]; } #endif static void setLayerBackgroundColor(PlatformLayer* layer, const Color& color) { CGColorRef bgColor = createCGColor(color); [layer setBackgroundColor:bgColor]; CGColorRelease(bgColor); } static void clearLayerBackgroundColor(PlatformLayer* layer) { [layer setBackgroundColor:0]; } static CALayer* getPresentationLayer(CALayer* layer) { CALayer* presLayer = [layer presentationLayer]; if (!presLayer) presLayer = layer; return presLayer; } static bool caValueFunctionSupported() { static bool sHaveValueFunction = [CAPropertyAnimation instancesRespondToSelector:@selector(setValueFunction:)]; return sHaveValueFunction; } static bool forceSoftwareAnimation() { static bool forceSoftwareAnimation = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebCoreForceSoftwareAnimation"]; return forceSoftwareAnimation; } GraphicsLayer::CompositingCoordinatesOrientation GraphicsLayer::compositingCoordinatesOrientation() { return CompositingCoordinatesBottomUp; } #ifndef NDEBUG bool GraphicsLayer::showDebugBorders() { static bool showDebugBorders = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebCoreLayerBorders"]; return showDebugBorders; } bool GraphicsLayer::showRepaintCounter() { static bool showRepaintCounter = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebCoreLayerRepaintCounter"]; return showRepaintCounter; } #endif 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", #ifndef NDEBUG nullValue, @"zPosition", #endif nil]; return actions; } GraphicsLayer* GraphicsLayer::createGraphicsLayer(GraphicsLayerClient* client) { return new GraphicsLayerCA(client); } GraphicsLayerCA::GraphicsLayerCA(GraphicsLayerClient* client) : GraphicsLayer(client) , m_contentLayerForImageOrVideo(false) { BEGIN_BLOCK_OBJC_EXCEPTIONS m_layer.adoptNS([[WebLayer alloc] init]); [m_layer.get() setLayerOwner:this]; #ifndef NDEBUG updateDebugIndicators(); #endif m_animationDelegate.adoptNS([[WebAnimationDelegate alloc] init]); [m_animationDelegate.get() setLayer:this]; END_BLOCK_OBJC_EXCEPTIONS } GraphicsLayerCA::~GraphicsLayerCA() { // Remove a inner layer if there is one. clearContents(); BEGIN_BLOCK_OBJC_EXCEPTIONS // Clean up the WK layer. if (m_layer) { WebLayer* layer = m_layer.get(); [layer setLayerOwner:nil]; [layer removeFromSuperlayer]; } if (m_transformLayer) [m_transformLayer.get() removeFromSuperlayer]; // animationDidStart: can fire after this, so we need to clear out the layer on the delegate. [m_animationDelegate.get() setLayer:0]; END_BLOCK_OBJC_EXCEPTIONS } void GraphicsLayerCA::setName(const String& name) { String longName = String::format("CALayer(%p) GraphicsLayer(%p) ", m_layer.get(), this) + name; GraphicsLayer::setName(longName); BEGIN_BLOCK_OBJC_EXCEPTIONS [m_layer.get() setName:name]; END_BLOCK_OBJC_EXCEPTIONS } NativeLayer GraphicsLayerCA::nativeLayer() const { return m_layer.get(); } void GraphicsLayerCA::addChild(GraphicsLayer* childLayer) { GraphicsLayer::addChild(childLayer); GraphicsLayerCA* childLayerCA = static_cast(childLayer); BEGIN_BLOCK_OBJC_EXCEPTIONS [hostLayerForSublayers() addSublayer:childLayerCA->layerForSuperlayer()]; END_BLOCK_OBJC_EXCEPTIONS } void GraphicsLayerCA::addChildAtIndex(GraphicsLayer* childLayer, int index) { GraphicsLayer::addChildAtIndex(childLayer, index); GraphicsLayerCA* childLayerCA = static_cast(childLayer); BEGIN_BLOCK_OBJC_EXCEPTIONS [hostLayerForSublayers() insertSublayer:childLayerCA->layerForSuperlayer() atIndex:index]; END_BLOCK_OBJC_EXCEPTIONS } void GraphicsLayerCA::addChildBelow(GraphicsLayer* childLayer, GraphicsLayer* sibling) { // FIXME: share code with base class ASSERT(childLayer != this); childLayer->removeFromParent(); bool found = false; for (unsigned i = 0; i < m_children.size(); i++) { if (sibling == m_children[i]) { m_children.insert(i, childLayer); found = true; break; } } childLayer->setParent(this); BEGIN_BLOCK_OBJC_EXCEPTIONS GraphicsLayerCA* childLayerCA = static_cast(childLayer); GraphicsLayerCA* siblingLayerCA = static_cast(sibling); if (found) [hostLayerForSublayers() insertSublayer:childLayerCA->layerForSuperlayer() below:siblingLayerCA->layerForSuperlayer()]; else { m_children.append(childLayer); [hostLayerForSublayers() addSublayer:childLayerCA->layerForSuperlayer()]; } END_BLOCK_OBJC_EXCEPTIONS } void GraphicsLayerCA::addChildAbove(GraphicsLayer* childLayer, GraphicsLayer* sibling) { // FIXME: share code with base class ASSERT(childLayer != this); childLayer->removeFromParent(); unsigned i; bool found = false; for (i = 0; i < m_children.size(); i++) { if (sibling == m_children[i]) { m_children.insert(i+1, childLayer); found = true; break; } } childLayer->setParent(this); BEGIN_BLOCK_OBJC_EXCEPTIONS GraphicsLayerCA* childLayerCA = static_cast(childLayer); GraphicsLayerCA* siblingLayerCA = static_cast(sibling); if (found) { [hostLayerForSublayers() insertSublayer:childLayerCA->layerForSuperlayer() above:siblingLayerCA->layerForSuperlayer()]; } else { m_children.append(childLayer); [hostLayerForSublayers() addSublayer:childLayerCA->layerForSuperlayer()]; } END_BLOCK_OBJC_EXCEPTIONS } bool GraphicsLayerCA::replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild) { // FIXME: share code with base class ASSERT(!newChild->parent()); bool found = false; for (unsigned i = 0; i < m_children.size(); i++) { if (oldChild == m_children[i]) { m_children[i] = newChild; found = true; break; } } if (found) { oldChild->setParent(0); newChild->removeFromParent(); newChild->setParent(this); BEGIN_BLOCK_OBJC_EXCEPTIONS GraphicsLayerCA* oldChildCA = static_cast(oldChild); GraphicsLayerCA* newChildCA = static_cast(newChild); [hostLayerForSublayers() replaceSublayer:oldChildCA->layerForSuperlayer() with:newChildCA->layerForSuperlayer()]; END_BLOCK_OBJC_EXCEPTIONS return true; } return false; } void GraphicsLayerCA::removeFromParent() { GraphicsLayer::removeFromParent(); BEGIN_BLOCK_OBJC_EXCEPTIONS [layerForSuperlayer() removeFromSuperlayer]; END_BLOCK_OBJC_EXCEPTIONS } void GraphicsLayerCA::setPosition(const FloatPoint& point) { // Don't short-circuit here, because position and anchor point are inter-dependent. GraphicsLayer::setPosition(point); // Position is offset on the layer by the layer anchor point. CGPoint posPoint = CGPointMake(m_position.x() + m_anchorPoint.x() * m_size.width(), m_position.y() + m_anchorPoint.y() * m_size.height()); BEGIN_BLOCK_OBJC_EXCEPTIONS [primaryLayer() setPosition:posPoint]; END_BLOCK_OBJC_EXCEPTIONS } void GraphicsLayerCA::setAnchorPoint(const FloatPoint3D& point) { // Don't short-circuit here, because position and anchor point are inter-dependent. bool zChanged = (point.z() != m_anchorPoint.z()); GraphicsLayer::setAnchorPoint(point); BEGIN_BLOCK_OBJC_EXCEPTIONS // set the value on the layer to the new transform. [primaryLayer() setAnchorPoint:FloatPoint(point.x(), point.y())]; if (zChanged) { #if HAVE_MODERN_QUARTZCORE [primaryLayer() setAnchorPointZ:m_anchorPoint.z()]; #endif } // Position depends on anchor point, so update it now. setPosition(m_position); END_BLOCK_OBJC_EXCEPTIONS } void GraphicsLayerCA::setSize(const FloatSize& size) { GraphicsLayer::setSize(size); CGRect rect = CGRectMake(0.0f, 0.0f, m_size.width(), m_size.height()); CGPoint centerPoint = CGPointMake(m_size.width() / 2.0f, m_size.height() / 2.0f); BEGIN_BLOCK_OBJC_EXCEPTIONS if (m_transformLayer) { [m_transformLayer.get() setBounds:rect]; // the anchor of the contents layer is always at 0.5, 0.5, so the position // is center-relative [m_layer.get() setPosition:centerPoint]; } bool needTiledLayer = requiresTiledLayer(m_size); if (needTiledLayer != m_usingTiledLayer) swapFromOrToTiledLayer(needTiledLayer); [m_layer.get() setBounds:rect]; // Note that we don't resize m_contentsLayer. It's up the caller to do that. END_BLOCK_OBJC_EXCEPTIONS // if we've changed the bounds, we need to recalculate the position // of the layer, taking anchor point into account setPosition(m_position); } void GraphicsLayerCA::setTransform(const TransformationMatrix& t) { GraphicsLayer::setTransform(t); BEGIN_BLOCK_OBJC_EXCEPTIONS CATransform3D transform; copyTransform(transform, t); [primaryLayer() setTransform:transform]; END_BLOCK_OBJC_EXCEPTIONS // Remove any old transition entries for transform. removeAllAnimationsForProperty(AnimatedPropertyWebkitTransform); // Even if we don't have a transition in the list, the layer may still have one. // This happens when we are setting the final transform value after an animation or // transition has ended. In removeAnimation we toss the entry from the list but don't // remove it from the list. That way we aren't in danger of displaying a stale transform // in the time between removing the animation and setting the new unanimated value. We // can't do this in removeAnimation because we don't know the new transform value there. String keyPath = propertyIdToString(AnimatedPropertyWebkitTransform); CALayer* layer = animatedLayer(AnimatedPropertyWebkitTransform); for (int i = 0; ; ++i) { String animName = keyPath + "_" + String::number(i); if (![layer animationForKey: animName]) break; [layer removeAnimationForKey:animName]; } } void GraphicsLayerCA::setChildrenTransform(const TransformationMatrix& t) { if (t == m_childrenTransform) return; GraphicsLayer::setChildrenTransform(t); CATransform3D transform; copyTransform(transform, t); BEGIN_BLOCK_OBJC_EXCEPTIONS // Set the value on the layer to the new transform. [primaryLayer() setSublayerTransform:transform]; END_BLOCK_OBJC_EXCEPTIONS } static void moveAnimation(AnimatedPropertyID property, CALayer* fromLayer, CALayer* toLayer) { String keyPath = GraphicsLayer::propertyIdToString(property); for (short index = 0; ; ++index) { String animName = keyPath + "_" + String::number(index); CAAnimation* anim = [fromLayer animationForKey:animName]; if (!anim) break; [anim retain]; [fromLayer removeAnimationForKey:animName]; [toLayer addAnimation:anim forKey:animName]; [anim release]; } } static void moveSublayers(CALayer* fromLayer, CALayer* toLayer) { NSArray* sublayersCopy = [[fromLayer sublayers] copy]; // Avoid mutation while enumerating, and keep the sublayers alive. NSEnumerator* childrenEnumerator = [sublayersCopy objectEnumerator]; CALayer* layer; while ((layer = [childrenEnumerator nextObject]) != nil) { [layer removeFromSuperlayer]; [toLayer addSublayer:layer]; } [sublayersCopy release]; } void GraphicsLayerCA::setPreserves3D(bool preserves3D) { GraphicsLayer::setPreserves3D(preserves3D); CGPoint point = CGPointMake(m_size.width() / 2.0f, m_size.height() / 2.0f); CGPoint centerPoint = CGPointMake(0.5f, 0.5f); BEGIN_BLOCK_OBJC_EXCEPTIONS Class transformLayerClass = NSClassFromString(@"CATransformLayer"); if (preserves3D && !m_transformLayer && transformLayerClass) { // Create the transform layer. m_transformLayer.adoptNS([[transformLayerClass alloc] init]); // Turn off default animations. [m_transformLayer.get() setStyle:[NSDictionary dictionaryWithObject:nullActionsDictionary() forKey:@"actions"]]; #ifndef NDEBUG [m_transformLayer.get() setName:[NSString stringWithFormat:@"Transform Layer CATransformLayer(%p) GraphicsLayer(%p)", m_transformLayer.get(), this]]; #endif // Copy the position from this layer. [m_transformLayer.get() setBounds:[m_layer.get() bounds]]; [m_transformLayer.get() setPosition:[m_layer.get() position]]; [m_transformLayer.get() setAnchorPoint:[m_layer.get() anchorPoint]]; #if HAVE_MODERN_QUARTZCORE [m_transformLayer.get() setAnchorPointZ:[m_layer.get() anchorPointZ]]; #endif [m_transformLayer.get() setContentsRect:[m_layer.get() contentsRect]]; #ifndef NDEBUG [m_transformLayer.get() setZPosition:[m_layer.get() zPosition]]; #endif // The contents layer is positioned at (0,0) relative to the transformLayer. [m_layer.get() setPosition:point]; [m_layer.get() setAnchorPoint:centerPoint]; #ifndef NDEBUG [m_layer.get() setZPosition:0.0f]; #endif // Transfer the transform over. [m_transformLayer.get() setTransform:[m_layer.get() transform]]; [m_layer.get() setTransform:CATransform3DIdentity]; // Transfer the opacity from the old layer to the transform layer. [m_transformLayer.get() setOpacity:m_opacity]; [m_layer.get() setOpacity:1]; // Move this layer to be a child of the transform layer. [[m_layer.get() superlayer] replaceSublayer:m_layer.get() with:m_transformLayer.get()]; [m_transformLayer.get() addSublayer:m_layer.get()]; moveAnimation(AnimatedPropertyWebkitTransform, m_layer.get(), m_transformLayer.get()); moveSublayers(m_layer.get(), m_transformLayer.get()); } else if (!preserves3D && m_transformLayer) { // Relace the transformLayer in the parent with this layer. [m_layer.get() removeFromSuperlayer]; [[m_transformLayer.get() superlayer] replaceSublayer:m_transformLayer.get() with:m_layer.get()]; moveAnimation(AnimatedPropertyWebkitTransform, m_transformLayer.get(), m_layer.get()); moveSublayers(m_transformLayer.get(), m_layer.get()); // Reset the layer position and transform. [m_layer.get() setPosition:[m_transformLayer.get() position]]; [m_layer.get() setAnchorPoint:[m_transformLayer.get() anchorPoint]]; #if HAVE_MODERN_QUARTZCORE [m_layer.get() setAnchorPointZ:[m_transformLayer.get() anchorPointZ]]; #endif [m_layer.get() setContentsRect:[m_transformLayer.get() contentsRect]]; [m_layer.get() setTransform:[m_transformLayer.get() transform]]; [m_layer.get() setOpacity:[m_transformLayer.get() opacity]]; #ifndef NDEBUG [m_layer.get() setZPosition:[m_transformLayer.get() zPosition]]; #endif // Release the transform layer. m_transformLayer = 0; } END_BLOCK_OBJC_EXCEPTIONS } void GraphicsLayerCA::setMasksToBounds(bool masksToBounds) { if (masksToBounds == m_masksToBounds) return; GraphicsLayer::setMasksToBounds(masksToBounds); BEGIN_BLOCK_OBJC_EXCEPTIONS [m_layer.get() setMasksToBounds:masksToBounds]; END_BLOCK_OBJC_EXCEPTIONS #ifndef NDEBUG updateDebugIndicators(); #endif } void GraphicsLayerCA::setDrawsContent(bool drawsContent) { if (drawsContent != m_drawsContent) { GraphicsLayer::setDrawsContent(drawsContent); bool needTiledLayer = requiresTiledLayer(m_size); if (needTiledLayer != m_usingTiledLayer) swapFromOrToTiledLayer(needTiledLayer); BEGIN_BLOCK_OBJC_EXCEPTIONS if (m_drawsContent) [m_layer.get() setNeedsDisplay]; else [m_layer.get() setContents:nil]; #ifndef NDEBUG updateDebugIndicators(); #endif END_BLOCK_OBJC_EXCEPTIONS } } void GraphicsLayerCA::setBackgroundColor(const Color& color, const Animation* transition, double beginTime) { GraphicsLayer::setBackgroundColor(color, transition, beginTime); BEGIN_BLOCK_OBJC_EXCEPTIONS if (!m_contentsLayer.get()) { WebLayer* colorLayer = [WebLayer layer]; #ifndef NDEBUG [colorLayer setName:@"Color Layer"]; #endif setContentsLayer(colorLayer); } if (transition && !transition->isEmptyOrZeroDuration()) { CALayer* presLayer = [m_contentsLayer.get() presentationLayer]; // If we don't have a presentationLayer, just use the CALayer if (!presLayer) presLayer = m_contentsLayer.get(); // Get the current value of the background color from the layer CGColorRef fromBackgroundColor = [presLayer backgroundColor]; CGColorRef bgColor = createCGColor(color); setBasicAnimation(AnimatedPropertyBackgroundColor, TransformOperation::NONE, 0, fromBackgroundColor, bgColor, true, transition, beginTime); CGColorRelease(bgColor); } else { removeAllAnimationsForProperty(AnimatedPropertyBackgroundColor); setLayerBackgroundColor(m_contentsLayer.get(), m_backgroundColor); } END_BLOCK_OBJC_EXCEPTIONS } void GraphicsLayerCA::clearBackgroundColor() { if (!m_contentLayerForImageOrVideo) setContentsLayer(0); else clearLayerBackgroundColor(m_contentsLayer.get()); } void GraphicsLayerCA::setContentsOpaque(bool opaque) { GraphicsLayer::setContentsOpaque(opaque); BEGIN_BLOCK_OBJC_EXCEPTIONS [m_layer.get() setOpaque:m_contentsOpaque]; END_BLOCK_OBJC_EXCEPTIONS } void GraphicsLayerCA::setBackfaceVisibility(bool visible) { if (m_backfaceVisibility == visible) return; GraphicsLayer::setBackfaceVisibility(visible); BEGIN_BLOCK_OBJC_EXCEPTIONS [m_layer.get() setDoubleSided:visible]; END_BLOCK_OBJC_EXCEPTIONS } bool GraphicsLayerCA::setOpacity(float opacity, const Animation* transition, double beginTime) { if (forceSoftwareAnimation()) return false; float clampedOpacity = max(0.0f, min(opacity, 1.0f)); bool opacitiesDiffer = (m_opacity != clampedOpacity); GraphicsLayer::setOpacity(clampedOpacity, transition, beginTime); int animIndex = findAnimationEntry(AnimatedPropertyOpacity, 0); // If we don't have a transition just set the opacity if (!transition || transition->isEmptyOrZeroDuration()) { // Three cases: // 1) no existing animation or transition: just set opacity // 2) existing transition: clear it and set opacity // 3) existing animation: just return // if (animIndex < 0 || m_animations[animIndex].isTransition()) { BEGIN_BLOCK_OBJC_EXCEPTIONS [primaryLayer() setOpacity:opacity]; if (animIndex >= 0) { removeAllAnimationsForProperty(AnimatedPropertyOpacity); animIndex = -1; } else { String keyPath = propertyIdToString(AnimatedPropertyOpacity); // FIXME: using hardcoded '0' here. We should be clearing all animations. For now there is only 1. String animName = keyPath + "_0"; [animatedLayer(AnimatedPropertyOpacity) removeAnimationForKey:animName]; } END_BLOCK_OBJC_EXCEPTIONS } else { // We have an animation, so don't set the opacity directly. return false; } } else { // At this point, we know we have a transition. But if it is the same and // the opacity value has not changed, don't do anything. if (!opacitiesDiffer && ((animIndex == -1) || (m_animations[animIndex].isTransition() && *(m_animations[animIndex].animation()) == *transition))) { return false; } } // If an animation is running, ignore this transition, but still save the value. if (animIndex >= 0 && !m_animations[animIndex].isTransition()) return false; bool didAnimate = false; BEGIN_BLOCK_OBJC_EXCEPTIONS NSNumber* fromOpacityValue = nil; NSNumber* toOpacityValue = [NSNumber numberWithFloat:opacity]; if (transition && !transition->isEmptyOrZeroDuration()) { CALayer* presLayer = getPresentationLayer(primaryLayer()); float fromOpacity = [presLayer opacity]; fromOpacityValue = [NSNumber numberWithFloat:fromOpacity]; setBasicAnimation(AnimatedPropertyOpacity, TransformOperation::NONE, 0, fromOpacityValue, toOpacityValue, true, transition, beginTime); didAnimate = true; } END_BLOCK_OBJC_EXCEPTIONS return didAnimate; } void GraphicsLayerCA::setNeedsDisplay() { BEGIN_BLOCK_OBJC_EXCEPTIONS if (drawsContent()) [m_layer.get() setNeedsDisplay]; END_BLOCK_OBJC_EXCEPTIONS } void GraphicsLayerCA::setNeedsDisplayInRect(const FloatRect& rect) { BEGIN_BLOCK_OBJC_EXCEPTIONS if (drawsContent()) [m_layer.get() setNeedsDisplayInRect:rect]; END_BLOCK_OBJC_EXCEPTIONS } bool GraphicsLayerCA::animateTransform(const TransformValueList& valueList, const IntSize& size, const Animation* anim, double beginTime, bool isTransition) { if (forceSoftwareAnimation() || !anim || anim->isEmptyOrZeroDuration() || valueList.size() < 2) return false; TransformValueList::FunctionList functionList; bool isValid, hasBigRotation; valueList.makeFunctionList(functionList, isValid, 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) && !caValueFunctionSupported()) return false; BEGIN_BLOCK_OBJC_EXCEPTIONS // Rules for animation: // // 1) If functionList is empty or we don't have a big rotation, we do a matrix animation. We could // use component animation for lists without a big rotation, but there is no need to, and this // is more efficient. // // 2) Otherwise we do a component hardware animation. bool isMatrixAnimation = !isValid || !hasBigRotation; // Set transform to identity since we are animating components and we need the base // to be the identity transform. TransformationMatrix t; CATransform3D toT3D; copyTransform(toT3D, t); [primaryLayer() setTransform:toT3D]; bool isKeyframe = valueList.size() > 2; // Iterate through the transform functions, sending an animation for each one. for (int functionIndex = 0; ; ++functionIndex) { if (functionIndex >= static_cast(functionList.size()) && !isMatrixAnimation) break; TransformOperation::OperationType opType = isMatrixAnimation ? TransformOperation::MATRIX_3D : functionList[functionIndex]; if (isKeyframe) { NSMutableArray* timesArray = [[NSMutableArray alloc] init]; NSMutableArray* valArray = [[NSMutableArray alloc] init]; NSMutableArray* tfArray = [[NSMutableArray alloc] init]; // Iterate through the keyframes, building arrays for the animation. for (Vector::const_iterator it = valueList.values().begin(); it != valueList.values().end(); ++it) { const TransformValue& curValue = (*it); // fill in the key time and timing function [timesArray addObject:[NSNumber numberWithFloat:curValue.key()]]; const TimingFunction* tf = 0; if (curValue.timingFunction()) tf = curValue.timingFunction(); else if (anim->isTimingFunctionSet()) tf = &anim->timingFunction(); CAMediaTimingFunction* timingFunction = getCAMediaTimingFunction(tf ? *tf : TimingFunction(LinearTimingFunction)); [tfArray addObject:timingFunction]; // fill in the function if (isMatrixAnimation) { TransformationMatrix t; curValue.value()->apply(size, t); CATransform3D cat; copyTransform(cat, t); [valArray addObject:[NSValue valueWithCATransform3D:cat]]; } else [valArray addObject:getTransformFunctionValue(curValue, functionIndex, size, opType)]; } // We toss the last tfArray value because it has to one shorter than the others. [tfArray removeLastObject]; setKeyframeAnimation(AnimatedPropertyWebkitTransform, opType, functionIndex, timesArray, valArray, tfArray, isTransition, anim, beginTime); [timesArray release]; [valArray release]; [tfArray release]; } else { // Is a transition id fromValue, toValue; if (isMatrixAnimation) { TransformationMatrix fromt, tot; valueList.at(0).value()->apply(size, fromt); valueList.at(1).value()->apply(size, tot); CATransform3D cat; copyTransform(cat, fromt); fromValue = [NSValue valueWithCATransform3D:cat]; copyTransform(cat, tot); toValue = [NSValue valueWithCATransform3D:cat]; } else { fromValue = getTransformFunctionValue(valueList.at(0), functionIndex, size, opType); toValue = getTransformFunctionValue(valueList.at(1), functionIndex, size, opType); } setBasicAnimation(AnimatedPropertyWebkitTransform, opType, functionIndex, fromValue, toValue, isTransition, anim, beginTime); } if (isMatrixAnimation) break; } END_BLOCK_OBJC_EXCEPTIONS return true; } bool GraphicsLayerCA::animateFloat(AnimatedPropertyID property, const FloatValueList& valueList, const Animation* animation, double beginTime) { if (forceSoftwareAnimation() || valueList.size() < 2) return false; // if there is already is an animation for this property and it hasn't changed, ignore it. int i = findAnimationEntry(property, 0); if (i >= 0 && *m_animations[i].animation() == *animation) { m_animations[i].setIsCurrent(); return false; } if (valueList.size() == 2) { float fromVal = valueList.at(0).value(); float toVal = valueList.at(1).value(); if (isnan(toVal) && isnan(fromVal)) return false; // initialize the property to 0 [animatedLayer(property) setValue:0 forKeyPath:propertyIdToString(property)]; setBasicAnimation(property, TransformOperation::NONE, 0, isnan(fromVal) ? nil : [NSNumber numberWithFloat:fromVal], isnan(toVal) ? nil : [NSNumber numberWithFloat:toVal], false, animation, beginTime); return true; } BEGIN_BLOCK_OBJC_EXCEPTIONS NSMutableArray* timesArray = [[NSMutableArray alloc] init]; NSMutableArray* valArray = [[NSMutableArray alloc] init]; NSMutableArray* tfArray = [[NSMutableArray alloc] init]; for (unsigned i = 0; i < valueList.values().size(); ++i) { const FloatValue& curValue = valueList.values()[i]; [timesArray addObject:[NSNumber numberWithFloat:curValue.key()]]; [valArray addObject:[NSNumber numberWithFloat:curValue.value()]]; const TimingFunction* tf = 0; if (curValue.timingFunction()) tf = curValue.timingFunction(); else if (animation->isTimingFunctionSet()) tf = &animation->timingFunction(); CAMediaTimingFunction* timingFunction = getCAMediaTimingFunction(tf ? *tf : TimingFunction()); [tfArray addObject:timingFunction]; } // We toss the last tfArray value because it has to one shorter than the others. [tfArray removeLastObject]; // Initialize the property to 0. [animatedLayer(property) setValue:0 forKeyPath:propertyIdToString(property)]; // Then set the animation. setKeyframeAnimation(property, TransformOperation::NONE, 0, timesArray, valArray, tfArray, false, animation, beginTime); [timesArray release]; [valArray release]; [tfArray release]; END_BLOCK_OBJC_EXCEPTIONS return true; } void GraphicsLayerCA::setContentsToImage(Image* image) { if (image) { BEGIN_BLOCK_OBJC_EXCEPTIONS { if (!m_contentsLayer.get()) { WebLayer* imageLayer = [WebLayer layer]; #ifndef NDEBUG [imageLayer setName:@"Image Layer"]; #endif setContentsLayer(imageLayer); } // 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.get() setMinificationFilter:kCAFilterTrilinear]; #endif CGImageRef theImage = image->nativeImageForCurrentFrame(); [m_contentsLayer.get() setContents:(id)theImage]; } END_BLOCK_OBJC_EXCEPTIONS } else setContentsLayer(0); m_contentLayerForImageOrVideo = (image != 0); } void GraphicsLayerCA::setContentsToVideo(PlatformLayer* videoLayer) { setContentsLayer(videoLayer); m_contentLayerForImageOrVideo = (videoLayer != 0); } void GraphicsLayerCA::clearContents() { if (m_contentLayerForImageOrVideo) { setContentsLayer(0); m_contentLayerForImageOrVideo = false; } } void GraphicsLayerCA::updateContentsRect() { if (m_client && m_contentsLayer) { IntRect contentRect = m_client->contentsBox(this); CGPoint point = CGPointMake(contentRect.x(), contentRect.y()); CGRect rect = CGRectMake(0.0f, 0.0f, contentRect.width(), contentRect.height()); BEGIN_BLOCK_OBJC_EXCEPTIONS [m_contentsLayer.get() setPosition:point]; [m_contentsLayer.get() setBounds:rect]; END_BLOCK_OBJC_EXCEPTIONS } } void GraphicsLayerCA::setBasicAnimation(AnimatedPropertyID property, TransformOperation::OperationType operationType, short index, void* fromVal, void* toVal, bool isTransition, const Animation* transition, double beginTime) { ASSERT(fromVal || toVal); WebLayer* layer = animatedLayer(property); BEGIN_BLOCK_OBJC_EXCEPTIONS // add an entry for this animation addAnimationEntry(property, index, isTransition, transition); String keyPath = propertyIdToString(property); String animName = keyPath + "_" + String::number(index); CABasicAnimation* basicAnim = [CABasicAnimation animationWithKeyPath:keyPath]; double duration = transition->duration(); if (duration <= 0) duration = cAnimationAlmostZeroDuration; float repeatCount = transition->iterationCount(); if (repeatCount == Animation::IterationCountInfinite) repeatCount = FLT_MAX; else if (transition->direction() == Animation::AnimationDirectionAlternate) repeatCount /= 2; [basicAnim setDuration:duration]; [basicAnim setRepeatCount:repeatCount]; [basicAnim setAutoreverses:transition->direction()]; [basicAnim setRemovedOnCompletion:NO]; // Note that currently transform is the only property which has animations // with an index > 0. [basicAnim setAdditive:property == AnimatedPropertyWebkitTransform]; [basicAnim setFillMode:@"extended"]; #if HAVE_MODERN_QUARTZCORE if (NSString* valueFunctionName = getValueFunctionNameForTransformOperation(operationType)) [basicAnim setValueFunction:[CAValueFunction functionWithName:valueFunctionName]]; #else UNUSED_PARAM(operationType); #endif // Set the delegate (and property value). int prop = isTransition ? property : AnimatedPropertyInvalid; [basicAnim setValue:[NSNumber numberWithInt:prop] forKey:WebAnimationCSSPropertyKey]; [basicAnim setDelegate:m_animationDelegate.get()]; NSTimeInterval bt = beginTime ? [layer convertTime:currentTimeToMediaTime(beginTime) fromLayer:nil] : 0; [basicAnim setBeginTime:bt]; if (fromVal) [basicAnim setFromValue:reinterpret_cast(fromVal)]; if (toVal) [basicAnim setToValue:reinterpret_cast(toVal)]; const TimingFunction* tf = 0; if (transition->isTimingFunctionSet()) tf = &transition->timingFunction(); CAMediaTimingFunction* timingFunction = getCAMediaTimingFunction(tf ? *tf : TimingFunction()); [basicAnim setTimingFunction:timingFunction]; // Send over the animation. [layer removeAnimationForKey:animName]; [layer addAnimation:basicAnim forKey:animName]; END_BLOCK_OBJC_EXCEPTIONS } void GraphicsLayerCA::setKeyframeAnimation(AnimatedPropertyID property, TransformOperation::OperationType operationType, short index, void* keys, void* values, void* timingFunctions, bool isTransition, const Animation* anim, double beginTime) { PlatformLayer* layer = animatedLayer(property); // Add an entry for this animation (which may change beginTime). addAnimationEntry(property, index, isTransition, anim); String keyPath = propertyIdToString(property); String animName = keyPath + "_" + String::number(index); BEGIN_BLOCK_OBJC_EXCEPTIONS CAKeyframeAnimation* keyframeAnim = [CAKeyframeAnimation animationWithKeyPath:keyPath]; 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; [keyframeAnim setDuration:duration]; [keyframeAnim setRepeatCount:repeatCount]; [keyframeAnim setAutoreverses:anim->direction()]; [keyframeAnim setRemovedOnCompletion:NO]; // The first animation is non-additive, all the rest are additive. // Note that currently transform is the only property which has animations // with an index > 0. [keyframeAnim setAdditive:(property == AnimatedPropertyWebkitTransform) ? YES : NO]; [keyframeAnim setFillMode:@"extended"]; #if HAVE_MODERN_QUARTZCORE if (NSString* valueFunctionName = getValueFunctionNameForTransformOperation(operationType)) [keyframeAnim setValueFunction:[CAValueFunction functionWithName:valueFunctionName]]; #else UNUSED_PARAM(operationType); #endif [keyframeAnim setKeyTimes:reinterpret_cast(keys)]; [keyframeAnim setValues:reinterpret_cast(values)]; // Set the delegate (and property value). int prop = isTransition ? property : AnimatedPropertyInvalid; [keyframeAnim setValue:[NSNumber numberWithInt: prop] forKey:WebAnimationCSSPropertyKey]; [keyframeAnim setDelegate:m_animationDelegate.get()]; NSTimeInterval bt = beginTime ? [layer convertTime:currentTimeToMediaTime(beginTime) fromLayer:nil] : 0; [keyframeAnim setBeginTime:bt]; // Set the timing functions, if any. if (timingFunctions != nil) [keyframeAnim setTimingFunctions:(id)timingFunctions]; // Send over the animation. [layer removeAnimationForKey:animName]; [layer addAnimation:keyframeAnim forKey:animName]; END_BLOCK_OBJC_EXCEPTIONS } void GraphicsLayerCA::suspendAnimations() { double t = currentTimeToMediaTime(currentTime()); [primaryLayer() setSpeed:0]; [primaryLayer() setTimeOffset:t]; } void GraphicsLayerCA::resumeAnimations() { [primaryLayer() setSpeed:1]; [primaryLayer() setTimeOffset:0]; } void GraphicsLayerCA::removeAnimation(int index, bool reset) { ASSERT(index >= 0); AnimatedPropertyID property = m_animations[index].property(); // Set the value of the property and remove the animation. String keyPath = propertyIdToString(property); String animName = keyPath + "_" + String::number(m_animations[index].index()); CALayer* layer = animatedLayer(property); BEGIN_BLOCK_OBJC_EXCEPTIONS // If we are not resetting, it means we are pausing. So we need to get the current presentation // value into the property before we remove the animation. if (!reset) { // Put the current value into the property. CALayer* presLayer = [layer presentationLayer]; if (presLayer) [layer setValue:[presLayer valueForKeyPath:keyPath] forKeyPath:keyPath]; // Make sure the saved values accurately reflect the value in the layer. id val = [layer valueForKeyPath:keyPath]; switch (property) { case AnimatedPropertyWebkitTransform: // FIXME: needs comment explaining why the m_transform is not obtained from the layer break; case AnimatedPropertyBackgroundColor: m_backgroundColor = Color(reinterpret_cast(val)); break; case AnimatedPropertyOpacity: m_opacity = [val floatValue]; break; case AnimatedPropertyInvalid: ASSERT_NOT_REACHED(); break; } } // If we have reached the end of an animation, we don't want to actually remove the // animation from the CALayer. At some point we will be setting the property to its // unanimated value and at that point we will remove the animation. That will avoid // any flashing between the time the animation is removed and the property is set. if (!reset || m_animations[index].isTransition()) [layer removeAnimationForKey:animName]; END_BLOCK_OBJC_EXCEPTIONS // Remove the animation entry. m_animations.remove(index); } PlatformLayer* GraphicsLayerCA::hostLayerForSublayers() const { return m_transformLayer ? m_transformLayer.get() : m_layer.get(); } PlatformLayer* GraphicsLayerCA::layerForSuperlayer() const { if (m_transformLayer) return m_transformLayer.get(); return m_layer.get(); } PlatformLayer* GraphicsLayerCA::platformLayer() const { return primaryLayer(); } #ifndef NDEBUG void GraphicsLayerCA::setDebugBackgroundColor(const Color& color) { BEGIN_BLOCK_OBJC_EXCEPTIONS if (color.isValid()) setLayerBackgroundColor(m_layer.get(), color); else clearLayerBackgroundColor(m_layer.get()); END_BLOCK_OBJC_EXCEPTIONS } void GraphicsLayerCA::setDebugBorder(const Color& color, float borderWidth) { BEGIN_BLOCK_OBJC_EXCEPTIONS if (color.isValid()) { setLayerBorderColor(m_layer.get(), color); [m_layer.get() setBorderWidth:borderWidth]; } else { clearBorderColor(m_layer.get()); [m_layer.get() setBorderWidth:0]; } END_BLOCK_OBJC_EXCEPTIONS } void GraphicsLayerCA::setZPosition(float position) { GraphicsLayer::setZPosition(position); BEGIN_BLOCK_OBJC_EXCEPTIONS [primaryLayer() setZPosition:position]; END_BLOCK_OBJC_EXCEPTIONS } #endif 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 userTiledLayer) { if (userTiledLayer == m_usingTiledLayer) return; CGSize tileSize = CGSizeMake(cTiledLayerTileSize, cTiledLayerTileSize); BEGIN_BLOCK_OBJC_EXCEPTIONS RetainPtr oldLayer = m_layer.get(); Class layerClass = userTiledLayer ? [WebTiledLayer self] : [WebLayer self]; m_layer.adoptNS([[layerClass alloc] init]); if (userTiledLayer) { WebTiledLayer* tiledLayer = (WebTiledLayer*)m_layer.get(); [tiledLayer setTileSize:tileSize]; [tiledLayer setLevelsOfDetail:1]; [tiledLayer setLevelsOfDetailBias:0]; if (GraphicsLayer::compositingCoordinatesOrientation() == GraphicsLayer::CompositingCoordinatesBottomUp) [tiledLayer setContentsGravity:@"bottomLeft"]; else [tiledLayer setContentsGravity:@"topLeft"]; } [m_layer.get() setLayerOwner:this]; [m_layer.get() setSublayers:[oldLayer.get() sublayers]]; [[oldLayer.get() superlayer] replaceSublayer:oldLayer.get() with:m_layer.get()]; [m_layer.get() setBounds:[oldLayer.get() bounds]]; [m_layer.get() setPosition:[oldLayer.get() position]]; [m_layer.get() setAnchorPoint:[oldLayer.get() anchorPoint]]; [m_layer.get() setOpaque:[oldLayer.get() isOpaque]]; [m_layer.get() setOpacity:[oldLayer.get() opacity]]; [m_layer.get() setTransform:[oldLayer.get() transform]]; [m_layer.get() setSublayerTransform:[oldLayer.get() sublayerTransform]]; [m_layer.get() setDoubleSided:[oldLayer.get() isDoubleSided]]; #ifndef NDEBUG [m_layer.get() setZPosition:[oldLayer.get() zPosition]]; #endif #ifndef NDEBUG String name = String::format("CALayer(%p) GraphicsLayer(%p) ", m_layer.get(), this) + m_name; [m_layer.get() setName:name]; #endif // move over animations moveAnimation(AnimatedPropertyWebkitTransform, oldLayer.get(), m_layer.get()); moveAnimation(AnimatedPropertyOpacity, oldLayer.get(), m_layer.get()); moveAnimation(AnimatedPropertyBackgroundColor, oldLayer.get(), m_layer.get()); // need to tell new layer to draw itself setNeedsDisplay(); END_BLOCK_OBJC_EXCEPTIONS m_usingTiledLayer = userTiledLayer; #ifndef NDEBUG updateDebugIndicators(); #endif } void GraphicsLayerCA::setContentsLayer(WebLayer* contentsLayer) { if (contentsLayer == m_contentsLayer) return; BEGIN_BLOCK_OBJC_EXCEPTIONS if (m_contentsLayer) { [m_contentsLayer.get() removeFromSuperlayer]; m_contentsLayer = 0; } if (contentsLayer) { // Turn off implicit animations on the inner layer. [contentsLayer setStyle:[NSDictionary dictionaryWithObject:nullActionsDictionary() forKey:@"actions"]]; m_contentsLayer.adoptNS([contentsLayer retain]); bool needToFlip = GraphicsLayer::compositingCoordinatesOrientation() == GraphicsLayer::CompositingCoordinatesBottomUp; CGPoint anchorPoint = needToFlip ? CGPointMake(0.0f, 1.0f) : CGPointZero; // If the layer world is flipped, we need to un-flip the contents layer if (needToFlip) { CATransform3D 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}; [m_contentsLayer.get() setTransform:flipper]; } [m_contentsLayer.get() setAnchorPoint:anchorPoint]; // Insert the content layer first. Video elements require this, because they have // shadow content that must display in front of the video. [m_layer.get() insertSublayer:m_contentsLayer.get() atIndex:0]; updateContentsRect(); #ifndef NDEBUG if (showDebugBorders()) { setLayerBorderColor(m_contentsLayer.get(), Color(0, 0, 128, 180)); [m_contentsLayer.get() setBorderWidth:1.0f]; } #endif } #ifndef NDEBUG updateDebugIndicators(); #endif END_BLOCK_OBJC_EXCEPTIONS } } // namespace WebCore #endif // USE(ACCELERATED_COMPOSITING)