Index: third_party/WebKit/WebCore/platform/graphics/mac/GraphicsLayerCA.mm |
=================================================================== |
--- third_party/WebKit/WebCore/platform/graphics/mac/GraphicsLayerCA.mm (revision 9383) |
+++ third_party/WebKit/WebCore/platform/graphics/mac/GraphicsLayerCA.mm (working copy) |
@@ -1,1472 +1,1477 @@ |
-/* |
- * 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" |
- |
-// Temporary |
-#import <QuartzCore/CAAnimationPrivate.h> |
-#import <QuartzCore/CAValueFunction.h> |
- |
-#import "Animation.h" |
-#import "BlockExceptions.h" |
-#import "CString.h" |
-#import "FloatRect.h" |
-#import "Image.h" |
-#import "PlatformString.h" |
-#import <QuartzCore/QuartzCore.h> |
-#import "RotateTransformOperation.h" |
-#import "ScaleTransformOperation.h" |
-#import "SystemTime.h" |
-#import "TranslateTransformOperation.h" |
-#import "WebLayer.h" |
-#import "WebTiledLayer.h" |
-#import <wtf/CurrentTime.h> |
- |
-using namespace std; |
- |
-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 { |
- |
-inline void copyTransform(CATransform3D& toT3D, const TransformationMatrix& t) |
-{ |
- toT3D = CATransform3DMakeAffineTransform(t); |
-} |
- |
-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: |
- return [NSNumber numberWithDouble:op ? (float) (static_cast<RotateTransformOperation*>(op)->angle() * M_PI / 180) : 0]; |
- |
- case TransformOperation::SCALE_X: |
- return [NSNumber numberWithDouble:op ? static_cast<ScaleTransformOperation*>(op)->x() : 0]; |
- case TransformOperation::SCALE_Y: |
- return [NSNumber numberWithDouble:op ? static_cast<ScaleTransformOperation*>(op)->y() : 0]; |
- |
- case TransformOperation::TRANSLATE_X: |
- return [NSNumber numberWithDouble:op ? static_cast<TranslateTransformOperation*>(op)->x(size) : 0]; |
- case TransformOperation::TRANSLATE_Y: |
- return [NSNumber numberWithDouble:op ? static_cast<TranslateTransformOperation*>(op)->x(size) : 0]; |
- |
- case TransformOperation::SCALE: |
- case TransformOperation::TRANSLATE: |
- case TransformOperation::SKEW_X: |
- case TransformOperation::SKEW_Y: |
- case TransformOperation::SKEW: |
- case TransformOperation::MATRIX: |
- 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; |
-} |
- |
-static String getAnimationValueFunction(TransformOperation::OperationType transformType) |
-{ |
- switch (transformType) { |
- case TransformOperation::ROTATE: |
- return "rotateZ"; |
- case TransformOperation::SCALE_X: |
- return "scaleX"; |
- case TransformOperation::SCALE_Y: |
- return "scaleY"; |
- case TransformOperation::TRANSLATE_X: |
- return "translateX"; |
- case TransformOperation::TRANSLATE_Y: |
- return "translateY"; |
- default: |
- return String(); |
- } |
-} |
- |
-static CAMediaTimingFunction* getCAMediaTimingFunction(const TimingFunction& timingFunction) |
-{ |
- switch (timingFunction.type()) { |
- case LinearTimingFunction: |
- return [CAMediaTimingFunction functionWithName:@"linear"]; |
- case CubicBezierTimingFunction: |
- return [CAMediaTimingFunction functionWithControlPoints:static_cast<float>(timingFunction.x1()) :static_cast<float>(timingFunction.y1()) |
- :static_cast<float>(timingFunction.x2()) :static_cast<float>(timingFunction.y2())]; |
- } |
- return 0; |
-} |
- |
-#ifndef NDEBUG |
-static void setLayerBorderColor(PlatformLayer* layer, const Color& color) |
-{ |
- CGColorRef borderColor = cgColor(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 = cgColor(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; |
-} |
- |
-bool GraphicsLayer::graphicsContextsFlipped() |
-{ |
- return true; |
-} |
- |
-#ifndef NDEBUG |
-bool GraphicsLayer::showDebugBorders() |
-{ |
- static int showDebugBorders = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebCoreLayerBorders"]; |
- return showDebugBorders; |
-} |
- |
-bool GraphicsLayer::showRepaintCounter() |
-{ |
- static int 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<GraphicsLayerCA*>(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<GraphicsLayerCA*>(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<GraphicsLayerCA*>(childLayer); |
- GraphicsLayerCA* siblingLayerCA = static_cast<GraphicsLayerCA*>(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<GraphicsLayerCA*>(childLayer); |
- GraphicsLayerCA* siblingLayerCA = static_cast<GraphicsLayerCA*>(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<GraphicsLayerCA*>(oldChild); |
- GraphicsLayerCA* newChildCA = static_cast<GraphicsLayerCA*>(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) |
- [primaryLayer() setAnchorPointZ:m_anchorPoint.z()]; |
- |
- // 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 |
- |
- if (preserves3D && !m_transformLayer) { |
- // Create the transform layer. |
- m_transformLayer.adoptNS([[CATransformLayer 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]]; |
- [m_transformLayer.get() setAnchorPointZ:[m_layer.get() anchorPointZ]]; |
- [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]]; |
- [m_layer.get() setAnchorPointZ:[m_transformLayer.get() anchorPointZ]]; |
- [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 |
- // Clobber any existing content. If necessary, CA will create backing store on the next display. |
- [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 |
- setHasContentsLayer(true); |
- |
- 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 = cgColor(color); |
- setBasicAnimation(AnimatedPropertyBackgroundColor, "", 0, fromBackgroundColor, bgColor, true, transition, beginTime); |
- CGColorRelease(bgColor); |
- } else { |
- removeAllAnimationsForProperty(AnimatedPropertyBackgroundColor); |
- setHasContentsLayer(true); |
- setLayerBackgroundColor(m_contentsLayer.get(), m_backgroundColor); |
- } |
- |
- END_BLOCK_OBJC_EXCEPTIONS |
-} |
- |
-void GraphicsLayerCA::clearBackgroundColor() |
-{ |
- if (!m_contentLayerForImageOrVideo) |
- setHasContentsLayer(false); |
- 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) |
-{ |
- 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, "", 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 (!anim || anim->isEmptyOrZeroDuration() || valueList.size() < 2) |
- return false; |
- |
- TransformValueList::FunctionList functionList; |
- bool isValid, hasBigRotation; |
- valueList.makeFunctionList(functionList, isValid, hasBigRotation); |
- |
- 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<int>(functionList.size()) && !isMatrixAnimation) |
- break; |
- |
- TransformOperation::OperationType opType = |
-#if ENABLE(3D_TRANSFORMS) |
- isMatrixAnimation ? TransformOperation::MATRIX_3D : functionList[functionIndex]; |
-#else |
- isMatrixAnimation ? TransformOperation::MATRIX : functionList[functionIndex]; |
-#endif |
- |
- 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<TransformValue>::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, getAnimationValueFunction(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, getAnimationValueFunction(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 (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, "", 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, "", 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) { |
- setHasContentsLayer(true); |
- |
- // FIXME: is image flipping really a property of the graphics context? |
- bool needToFlip = GraphicsLayer::graphicsContextsFlipped(); |
- CGPoint anchorPoint = needToFlip ? CGPointMake(0.0f, 1.0f) : CGPointZero; |
- |
- BEGIN_BLOCK_OBJC_EXCEPTIONS |
- { |
- CGImageRef theImage = image->nativeImageForCurrentFrame(); |
- // 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 |
- 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]; |
- [m_contentsLayer.get() setContents:(id)theImage]; |
- } |
- END_BLOCK_OBJC_EXCEPTIONS |
- } else |
- setHasContentsLayer(false); |
- |
- m_contentLayerForImageOrVideo = (image != 0); |
-} |
- |
-void GraphicsLayerCA::setContentsToVideo(PlatformLayer* videoLayer) |
-{ |
- setContentsLayer(videoLayer); |
- m_contentLayerForImageOrVideo = (videoLayer != 0); |
-} |
- |
-void GraphicsLayerCA::clearContents() |
-{ |
- if (m_contentLayerForImageOrVideo) { |
- setHasContentsLayer(false); |
- 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, const String& valueFunction, 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 < 0) |
- repeatCount = FLT_MAX; |
- else if (transition->direction()) // If we alternate, the number of cycles is halved |
- 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"]; |
- [basicAnim setValueFunction:[CAValueFunction functionWithName:valueFunction]]; |
- |
- // 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<id>(fromVal)]; |
- if (toVal) |
- [basicAnim setToValue:reinterpret_cast<id>(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, const String& valueFunction, 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 < 0) |
- repeatCount = FLT_MAX; |
- else if (anim->direction()) |
- 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"]; |
- [keyframeAnim setValueFunction:[CAValueFunction functionWithName:valueFunction]]; |
- |
- [keyframeAnim setKeyTimes:reinterpret_cast<id>(keys)]; |
- [keyframeAnim setValues:reinterpret_cast<id>(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<CGColorRef>(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<CALayer> 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::graphicsContextsFlipped()) |
- [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::setHasContentsLayer(bool hasLayer) |
-{ |
- BEGIN_BLOCK_OBJC_EXCEPTIONS |
- |
- if (hasLayer && !m_contentsLayer) { |
- // create the inner layer |
- WebLayer* contentsLayer = [WebLayer layer]; |
-#ifndef NDEBUG |
- [contentsLayer setName:@"Contents Layer"]; |
-#endif |
- setContentsLayer(contentsLayer); |
- |
- } else if (!hasLayer && m_contentsLayer) |
- setContentsLayer(0); |
- |
- END_BLOCK_OBJC_EXCEPTIONS |
-} |
- |
-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]); |
- [m_contentsLayer.get() setAnchorPoint:CGPointZero]; |
- [m_layer.get() addSublayer:m_contentsLayer.get()]; |
- |
- updateContentsRect(); |
- |
- // Set contents to nil if the layer does not draw its own content. |
- if (m_client && !drawsContent()) |
- [m_layer.get() setContents:nil]; |
- |
-#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) |
+/* |
+ * 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" |
+ |
+// Temporary |
+#import <QuartzCore/CAAnimationPrivate.h> |
+#import <QuartzCore/CAValueFunction.h> |
+ |
+#import "Animation.h" |
+#import "BlockExceptions.h" |
+#import "CString.h" |
+#import "FloatRect.h" |
+#import "Image.h" |
+#import "PlatformString.h" |
+#import <QuartzCore/QuartzCore.h> |
+#import "RotateTransformOperation.h" |
+#import "ScaleTransformOperation.h" |
+#import "SystemTime.h" |
+#import "TranslateTransformOperation.h" |
+#import "WebLayer.h" |
+#import "WebTiledLayer.h" |
+#import <wtf/CurrentTime.h> |
+ |
+using namespace std; |
+ |
+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 { |
+ |
+inline void copyTransform(CATransform3D& toT3D, const TransformationMatrix& t) |
+{ |
+ toT3D = CATransform3DMakeAffineTransform(t); |
+} |
+ |
+TransformationMatrix CAToTransform3D(const CATransform3D& fromT3D) |
+{ |
+ return TransformationMatrix(CATransform3DGetAffineTransform(fromT3D)); |
+} |
+ |
+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: |
+ return [NSNumber numberWithDouble:op ? (float) (static_cast<RotateTransformOperation*>(op)->angle() * M_PI / 180) : 0]; |
+ |
+ case TransformOperation::SCALE_X: |
+ return [NSNumber numberWithDouble:op ? static_cast<ScaleTransformOperation*>(op)->x() : 0]; |
+ case TransformOperation::SCALE_Y: |
+ return [NSNumber numberWithDouble:op ? static_cast<ScaleTransformOperation*>(op)->y() : 0]; |
+ |
+ case TransformOperation::TRANSLATE_X: |
+ return [NSNumber numberWithDouble:op ? static_cast<TranslateTransformOperation*>(op)->x(size) : 0]; |
+ case TransformOperation::TRANSLATE_Y: |
+ return [NSNumber numberWithDouble:op ? static_cast<TranslateTransformOperation*>(op)->x(size) : 0]; |
+ |
+ case TransformOperation::SCALE: |
+ case TransformOperation::TRANSLATE: |
+ case TransformOperation::SKEW_X: |
+ case TransformOperation::SKEW_Y: |
+ case TransformOperation::SKEW: |
+ case TransformOperation::MATRIX: |
+ 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; |
+} |
+ |
+static String getAnimationValueFunction(TransformOperation::OperationType transformType) |
+{ |
+ switch (transformType) { |
+ case TransformOperation::ROTATE: |
+ return "rotateZ"; |
+ case TransformOperation::SCALE_X: |
+ return "scaleX"; |
+ case TransformOperation::SCALE_Y: |
+ return "scaleY"; |
+ case TransformOperation::TRANSLATE_X: |
+ return "translateX"; |
+ case TransformOperation::TRANSLATE_Y: |
+ return "translateY"; |
+ default: |
+ return String(); |
+ } |
+} |
+ |
+static CAMediaTimingFunction* getCAMediaTimingFunction(const TimingFunction& timingFunction) |
+{ |
+ switch (timingFunction.type()) { |
+ case LinearTimingFunction: |
+ return [CAMediaTimingFunction functionWithName:@"linear"]; |
+ case CubicBezierTimingFunction: |
+ return [CAMediaTimingFunction functionWithControlPoints:static_cast<float>(timingFunction.x1()) :static_cast<float>(timingFunction.y1()) |
+ :static_cast<float>(timingFunction.x2()) :static_cast<float>(timingFunction.y2())]; |
+ } |
+ return 0; |
+} |
+ |
+#ifndef NDEBUG |
+static void setLayerBorderColor(PlatformLayer* layer, const Color& color) |
+{ |
+ CGColorRef borderColor = cgColor(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 = cgColor(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; |
+} |
+ |
+bool GraphicsLayer::graphicsContextsFlipped() |
+{ |
+ return true; |
+} |
+ |
+#ifndef NDEBUG |
+bool GraphicsLayer::showDebugBorders() |
+{ |
+ static int showDebugBorders = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebCoreLayerBorders"]; |
+ return showDebugBorders; |
+} |
+ |
+bool GraphicsLayer::showRepaintCounter() |
+{ |
+ static int 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<GraphicsLayerCA*>(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<GraphicsLayerCA*>(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<GraphicsLayerCA*>(childLayer); |
+ GraphicsLayerCA* siblingLayerCA = static_cast<GraphicsLayerCA*>(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<GraphicsLayerCA*>(childLayer); |
+ GraphicsLayerCA* siblingLayerCA = static_cast<GraphicsLayerCA*>(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<GraphicsLayerCA*>(oldChild); |
+ GraphicsLayerCA* newChildCA = static_cast<GraphicsLayerCA*>(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) |
+ [primaryLayer() setAnchorPointZ:m_anchorPoint.z()]; |
+ |
+ // 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 |
+ |
+ if (preserves3D && !m_transformLayer) { |
+ // Create the transform layer. |
+ m_transformLayer.adoptNS([[CATransformLayer 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]]; |
+ [m_transformLayer.get() setAnchorPointZ:[m_layer.get() anchorPointZ]]; |
+ [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]]; |
+ [m_layer.get() setAnchorPointZ:[m_transformLayer.get() anchorPointZ]]; |
+ [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 |
+ // Clobber any existing content. If necessary, CA will create backing store on the next display. |
+ [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 |
+ setHasContentsLayer(true); |
+ |
+ 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 = cgColor(color); |
+ setBasicAnimation(AnimatedPropertyBackgroundColor, "", 0, fromBackgroundColor, bgColor, true, transition, beginTime); |
+ CGColorRelease(bgColor); |
+ } else { |
+ removeAllAnimationsForProperty(AnimatedPropertyBackgroundColor); |
+ setHasContentsLayer(true); |
+ setLayerBackgroundColor(m_contentsLayer.get(), m_backgroundColor); |
+ } |
+ |
+ END_BLOCK_OBJC_EXCEPTIONS |
+} |
+ |
+void GraphicsLayerCA::clearBackgroundColor() |
+{ |
+ if (!m_contentLayerForImageOrVideo) |
+ setHasContentsLayer(false); |
+ 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) |
+{ |
+ 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, "", 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 (!anim || anim->isEmptyOrZeroDuration() || valueList.size() < 2) |
+ return false; |
+ |
+ TransformValueList::FunctionList functionList; |
+ bool isValid, hasBigRotation; |
+ valueList.makeFunctionList(functionList, isValid, hasBigRotation); |
+ |
+ 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<int>(functionList.size()) && !isMatrixAnimation) |
+ break; |
+ |
+ TransformOperation::OperationType opType = |
+#if ENABLE(3D_TRANSFORMS) |
+ isMatrixAnimation ? TransformOperation::MATRIX_3D : functionList[functionIndex]; |
+#else |
+ isMatrixAnimation ? TransformOperation::MATRIX : functionList[functionIndex]; |
+#endif |
+ |
+ 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<TransformValue>::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, getAnimationValueFunction(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, getAnimationValueFunction(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 (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, "", 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, "", 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) { |
+ setHasContentsLayer(true); |
+ |
+ // FIXME: is image flipping really a property of the graphics context? |
+ bool needToFlip = GraphicsLayer::graphicsContextsFlipped(); |
+ CGPoint anchorPoint = needToFlip ? CGPointMake(0.0f, 1.0f) : CGPointZero; |
+ |
+ BEGIN_BLOCK_OBJC_EXCEPTIONS |
+ { |
+ CGImageRef theImage = image->nativeImageForCurrentFrame(); |
+ // 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 |
+ 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]; |
+ [m_contentsLayer.get() setContents:(id)theImage]; |
+ } |
+ END_BLOCK_OBJC_EXCEPTIONS |
+ } else |
+ setHasContentsLayer(false); |
+ |
+ m_contentLayerForImageOrVideo = (image != 0); |
+} |
+ |
+void GraphicsLayerCA::setContentsToVideo(PlatformLayer* videoLayer) |
+{ |
+ setContentsLayer(videoLayer); |
+ m_contentLayerForImageOrVideo = (videoLayer != 0); |
+} |
+ |
+void GraphicsLayerCA::clearContents() |
+{ |
+ if (m_contentLayerForImageOrVideo) { |
+ setHasContentsLayer(false); |
+ 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, const String& valueFunction, 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 < 0) |
+ repeatCount = FLT_MAX; |
+ else if (transition->direction()) // If we alternate, the number of cycles is halved |
+ 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"]; |
+ [basicAnim setValueFunction:[CAValueFunction functionWithName:valueFunction]]; |
+ |
+ // 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<id>(fromVal)]; |
+ if (toVal) |
+ [basicAnim setToValue:reinterpret_cast<id>(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, const String& valueFunction, 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 < 0) |
+ repeatCount = FLT_MAX; |
+ else if (anim->direction()) |
+ 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"]; |
+ [keyframeAnim setValueFunction:[CAValueFunction functionWithName:valueFunction]]; |
+ |
+ [keyframeAnim setKeyTimes:reinterpret_cast<id>(keys)]; |
+ [keyframeAnim setValues:reinterpret_cast<id>(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<CGColorRef>(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<CALayer> 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::graphicsContextsFlipped()) |
+ [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::setHasContentsLayer(bool hasLayer) |
+{ |
+ BEGIN_BLOCK_OBJC_EXCEPTIONS |
+ |
+ if (hasLayer && !m_contentsLayer) { |
+ // create the inner layer |
+ WebLayer* contentsLayer = [WebLayer layer]; |
+#ifndef NDEBUG |
+ [contentsLayer setName:@"Contents Layer"]; |
+#endif |
+ setContentsLayer(contentsLayer); |
+ |
+ } else if (!hasLayer && m_contentsLayer) |
+ setContentsLayer(0); |
+ |
+ END_BLOCK_OBJC_EXCEPTIONS |
+} |
+ |
+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]); |
+ [m_contentsLayer.get() setAnchorPoint:CGPointZero]; |
+ [m_layer.get() addSublayer:m_contentsLayer.get()]; |
+ |
+ updateContentsRect(); |
+ |
+ // Set contents to nil if the layer does not draw its own content. |
+ if (m_client && !drawsContent()) |
+ [m_layer.get() setContents:nil]; |
+ |
+#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) |