diff options
author | Ben Murdoch <benm@google.com> | 2009-08-11 17:01:47 +0100 |
---|---|---|
committer | Ben Murdoch <benm@google.com> | 2009-08-11 18:21:02 +0100 |
commit | 0bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5 (patch) | |
tree | 2943df35f62d885c89d01063cc528dd73b480fea /WebCore/platform/graphics/mac | |
parent | 7e7a70bfa49a1122b2597a1e6367d89eb4035eca (diff) | |
download | external_webkit-0bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5.zip external_webkit-0bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5.tar.gz external_webkit-0bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5.tar.bz2 |
Merge in WebKit r47029.
Diffstat (limited to 'WebCore/platform/graphics/mac')
-rw-r--r-- | WebCore/platform/graphics/mac/ColorMac.h | 3 | ||||
-rw-r--r-- | WebCore/platform/graphics/mac/ColorMac.mm | 60 | ||||
-rw-r--r-- | WebCore/platform/graphics/mac/FontPlatformData.h | 6 | ||||
-rw-r--r-- | WebCore/platform/graphics/mac/FontPlatformDataMac.mm | 9 | ||||
-rw-r--r-- | WebCore/platform/graphics/mac/GraphicsLayerCA.h | 177 | ||||
-rw-r--r-- | WebCore/platform/graphics/mac/GraphicsLayerCA.mm | 1906 | ||||
-rw-r--r-- | WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h | 35 | ||||
-rw-r--r-- | WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm | 353 | ||||
-rw-r--r-- | WebCore/platform/graphics/mac/WebLayer.h | 7 | ||||
-rw-r--r-- | WebCore/platform/graphics/mac/WebLayer.mm | 36 | ||||
-rw-r--r-- | WebCore/platform/graphics/mac/WebTiledLayer.mm | 9 |
11 files changed, 1626 insertions, 975 deletions
diff --git a/WebCore/platform/graphics/mac/ColorMac.h b/WebCore/platform/graphics/mac/ColorMac.h index 8a5ce31..b68b157 100644 --- a/WebCore/platform/graphics/mac/ColorMac.h +++ b/WebCore/platform/graphics/mac/ColorMac.h @@ -46,6 +46,9 @@ namespace WebCore { bool usesTestModeFocusRingColor(); void setUsesTestModeFocusRingColor(bool); + // Focus ring color used for testing purposes. + RGBA32 oldAquaFocusRingColor(); + } #endif diff --git a/WebCore/platform/graphics/mac/ColorMac.mm b/WebCore/platform/graphics/mac/ColorMac.mm index 05dd78d..c8ea9b1 100644 --- a/WebCore/platform/graphics/mac/ColorMac.mm +++ b/WebCore/platform/graphics/mac/ColorMac.mm @@ -29,18 +29,26 @@ #import <wtf/RetainPtr.h> #import <wtf/StdLibExtras.h> -@interface WebCoreControlTintObserver : NSObject -+ (void)controlTintDidChange; -@end - namespace WebCore { // NSColor calls don't throw, so no need to block Cocoa exceptions in this file -static RGBA32 oldAquaFocusRingColor = 0xFF7DADD9; -static RGBA32 systemFocusRingColor; static bool useOldAquaFocusRingColor; +RGBA32 oldAquaFocusRingColor() +{ + return 0xFF7DADD9; +} + +void setUsesTestModeFocusRingColor(bool newValue) +{ + useOldAquaFocusRingColor = newValue; +} + +bool usesTestModeFocusRingColor() +{ + return useOldAquaFocusRingColor; +} static RGBA32 makeRGBAFromNSColor(NSColor *c) { @@ -119,42 +127,4 @@ CGColorRef createCGColor(const Color& c) return CGColorFromNSColor(nsColor(c)); } -Color focusRingColor() -{ - static bool tintIsKnown = false; - if (!tintIsKnown) { - [[NSNotificationCenter defaultCenter] addObserver:[WebCoreControlTintObserver class] - selector:@selector(controlTintDidChange) - name:NSControlTintDidChangeNotification - object:NSApp]; - [WebCoreControlTintObserver controlTintDidChange]; - tintIsKnown = true; - } - - if (usesTestModeFocusRingColor()) - return oldAquaFocusRingColor; - - return systemFocusRingColor; -} - -bool usesTestModeFocusRingColor() -{ - return useOldAquaFocusRingColor; -} - -void setUsesTestModeFocusRingColor(bool newValue) -{ - useOldAquaFocusRingColor = newValue; -} - -} - -@implementation WebCoreControlTintObserver - -+ (void)controlTintDidChange -{ - NSColor *color = [[NSColor keyboardFocusIndicatorColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace]; - WebCore::systemFocusRingColor = WebCore::makeRGBAFromNSColor(color); -} - -@end +} // namespace WebCore diff --git a/WebCore/platform/graphics/mac/FontPlatformData.h b/WebCore/platform/graphics/mac/FontPlatformData.h index 1b7b884..faf5f2e 100644 --- a/WebCore/platform/graphics/mac/FontPlatformData.h +++ b/WebCore/platform/graphics/mac/FontPlatformData.h @@ -45,6 +45,8 @@ typedef UInt32 ATSUFontID; namespace WebCore { +class String; + #ifndef BUILDING_ON_TIGER inline CTFontRef toCTFontRef(NSFont *nsFont) { return reinterpret_cast<CTFontRef>(nsFont); } #endif @@ -118,6 +120,10 @@ struct FontPlatformData { CGFontRef cgFont() const { return m_cgFont; } #endif +#ifndef NDEBUG + String description() const; +#endif + private: static NSFont *hashTableDeletedFontValue() { return reinterpret_cast<NSFont *>(-1); } diff --git a/WebCore/platform/graphics/mac/FontPlatformDataMac.mm b/WebCore/platform/graphics/mac/FontPlatformDataMac.mm index 83da4a9..0118c8b 100644 --- a/WebCore/platform/graphics/mac/FontPlatformDataMac.mm +++ b/WebCore/platform/graphics/mac/FontPlatformDataMac.mm @@ -23,6 +23,7 @@ #import "config.h" #import "FontPlatformData.h" +#import "PlatformString.h" #import "WebCoreSystemInterface.h" #import <AppKit/NSFont.h> @@ -107,4 +108,12 @@ bool FontPlatformData::allowsLigatures() const return ![[m_font coveredCharacterSet] characterIsMember:'a']; } +#ifndef NDEBUG +String FontPlatformData::description() const +{ + RetainPtr<CFStringRef> cgFontDescription(AdoptCF, CFCopyDescription(cgFont())); + return String(cgFontDescription.get()) + " " + String::number(m_size) + (m_syntheticBold ? " synthetic bold" : "") + (m_syntheticOblique ? " syntheitic oblique" : ""); +} +#endif + } // namespace WebCore diff --git a/WebCore/platform/graphics/mac/GraphicsLayerCA.h b/WebCore/platform/graphics/mac/GraphicsLayerCA.h index 50138d5..ebdc6ac 100644 --- a/WebCore/platform/graphics/mac/GraphicsLayerCA.h +++ b/WebCore/platform/graphics/mac/GraphicsLayerCA.h @@ -29,8 +29,15 @@ #if USE(ACCELERATED_COMPOSITING) #include "GraphicsLayer.h" +#include "StringHash.h" +#include <wtf/HashSet.h> #include <wtf/RetainPtr.h> +@class CABasicAnimation; +@class CAKeyframeAnimation; +@class CALayer; +@class CAMediaTimingFunction; +@class CAPropertyAnimation; @class WebAnimationDelegate; @class WebLayer; @@ -67,66 +74,192 @@ public: virtual void setMasksToBounds(bool); virtual void setDrawsContent(bool); - virtual void setBackgroundColor(const Color&, const Animation* anim = 0, double beginTime = 0); + virtual void setBackgroundColor(const Color&); virtual void clearBackgroundColor(); virtual void setContentsOpaque(bool); virtual void setBackfaceVisibility(bool); // return true if we started an animation - virtual bool setOpacity(float, const Animation* anim = 0, double beginTime = 0); + virtual void setOpacity(float); virtual void setNeedsDisplay(); virtual void setNeedsDisplayInRect(const FloatRect&); - virtual void suspendAnimations(); + virtual void setContentsRect(const IntRect&); + + virtual void suspendAnimations(double time); virtual void resumeAnimations(); - virtual bool animateTransform(const TransformValueList&, const IntSize&, const Animation*, double beginTime, bool isTransition); - virtual bool animateFloat(AnimatedPropertyID, const FloatValueList&, const Animation*, double beginTime); - + virtual bool addAnimation(const KeyframeValueList&, const IntSize& boxSize, const Animation*, const String& keyframesName, double beginTime); + virtual void removeAnimationsForProperty(AnimatedPropertyID); + virtual void removeAnimationsForKeyframes(const String& keyframesName); + virtual void pauseAnimation(const String& keyframesName); + virtual void setContentsToImage(Image*); virtual void setContentsToVideo(PlatformLayer*); - virtual void clearContents(); - virtual void updateContentsRect(); - virtual PlatformLayer* platformLayer() const; #ifndef NDEBUG virtual void setDebugBackgroundColor(const Color&); virtual void setDebugBorder(const Color&, float borderWidth); - virtual void setZPosition(float); #endif + virtual void setGeometryOrientation(CompositingCoordinatesOrientation); + + void recursiveCommitChanges(); + void commitLayerChanges(); + + virtual void syncCompositingState(); + +protected: + virtual void setOpacityInternal(float); + private: - WebLayer* primaryLayer() const { return m_transformLayer.get() ? m_transformLayer.get() : m_layer.get(); } + void updateOpacityOnLayer(); + + WebLayer* primaryLayer() const { return m_transformLayer.get() ? m_transformLayer.get() : m_layer.get(); } WebLayer* hostLayerForSublayers() const; WebLayer* layerForSuperlayer() const; + CALayer* animatedLayer(AnimatedPropertyID property) const; - WebLayer* animatedLayer(AnimatedPropertyID property) const - { - return (property == AnimatedPropertyBackgroundColor) ? m_contentsLayer.get() : primaryLayer(); - } + bool createAnimationFromKeyframes(const KeyframeValueList&, const Animation*, const String& keyframesName, double beginTime); + bool createTransformAnimationsFromKeyframes(const KeyframeValueList&, const Animation*, const String& keyframesName, double beginTime, const IntSize& boxSize); - void setBasicAnimation(AnimatedPropertyID, TransformOperation::OperationType, short index, void* fromVal, void* toVal, bool isTransition, const Animation*, double time); - void setKeyframeAnimation(AnimatedPropertyID, TransformOperation::OperationType, short index, void* keys, void* values, void* timingFunctions, bool isTransition, const Animation*, double time); + // Return autoreleased animation (use RetainPtr?) + CABasicAnimation* createBasicAnimation(const Animation*, AnimatedPropertyID, bool additive); + CAKeyframeAnimation* createKeyframeAnimation(const Animation*, AnimatedPropertyID, bool additive); + void setupAnimation(CAPropertyAnimation*, const Animation*, bool additive); + + CAMediaTimingFunction* timingFunctionForAnimationValue(const AnimationValue*, const Animation*); + + bool setAnimationEndpoints(const KeyframeValueList&, const Animation*, CABasicAnimation*); + bool setAnimationKeyframes(const KeyframeValueList&, const Animation*, CAKeyframeAnimation*); - virtual void removeAnimation(int index, bool reset); + bool setTransformAnimationEndpoints(const KeyframeValueList&, const Animation*, CABasicAnimation*, int functionIndex, TransformOperation::OperationType, bool isMatrixAnimation, const IntSize& boxSize); + bool setTransformAnimationKeyframes(const KeyframeValueList&, const Animation*, CAKeyframeAnimation*, int functionIndex, TransformOperation::OperationType, bool isMatrixAnimation, const IntSize& boxSize); + + bool animationIsRunning(const String& keyframesName) const + { + return m_runningKeyframeAnimations.find(keyframesName) != m_runningKeyframeAnimations.end(); + } bool requiresTiledLayer(const FloatSize&) const; void swapFromOrToTiledLayer(bool useTiledLayer); - void setContentsLayer(WebLayer*); - WebLayer* contentsLayer() const { return m_contentsLayer.get(); } + CompositingCoordinatesOrientation defaultContentsOrientation() const; + void updateContentsTransform(); + void setupContentsLayer(CALayer*); + CALayer* contentsLayer() const { return m_contentsLayer.get(); } + + // All these "update" methods will be called inside a BEGIN_BLOCK_OBJC_EXCEPTIONS/END_BLOCK_OBJC_EXCEPTIONS block. + void updateSublayerList(); + void updateLayerPosition(); + void updateLayerSize(); + void updateAnchorPoint(); + void updateTransform(); + void updateChildrenTransform(); + void updateMasksToBounds(); + void updateContentsOpaque(); + void updateBackfaceVisibility(); + void updateLayerPreserves3D(); + void updateLayerDrawsContent(); + void updateLayerBackgroundColor(); + + void updateContentsImage(); + void updateContentsVideo(); + void updateContentsRect(); + void updateGeometryOrientation(); + + void updateLayerAnimations(); + + void setAnimationOnLayer(CAPropertyAnimation*, AnimatedPropertyID, int index, double beginTime); + bool removeAnimationFromLayer(AnimatedPropertyID, int index); + void pauseAnimationOnLayer(AnimatedPropertyID, int index); + + enum LayerChange { + NoChange = 0, + NameChanged = 1 << 1, + ChildrenChanged = 1 << 2, // also used for content layer, and preserves-3d, and size if tiling changes? + PositionChanged = 1 << 3, + AnchorPointChanged = 1 << 4, + SizeChanged = 1 << 5, + TransformChanged = 1 << 6, + ChildrenTransformChanged = 1 << 7, + Preserves3DChanged = 1 << 8, + MasksToBoundsChanged = 1 << 9, + DrawsContentChanged = 1 << 10, // need this? + BackgroundColorChanged = 1 << 11, + ContentsOpaqueChanged = 1 << 12, + BackfaceVisibilityChanged = 1 << 13, + OpacityChanged = 1 << 14, + AnimationChanged = 1 << 15, + DirtyRectsChanged = 1 << 16, + ContentsImageChanged = 1 << 17, + ContentsVideoChanged = 1 << 18, + ContentsRectChanged = 1 << 19, + GeometryOrientationChanged = 1 << 20 + }; + typedef unsigned LayerChangeFlags; + void noteLayerPropertyChanged(LayerChangeFlags flags); + + void repaintLayerDirtyRects(); + RetainPtr<WebLayer> m_layer; RetainPtr<WebLayer> m_transformLayer; - RetainPtr<WebLayer> m_contentsLayer; + RetainPtr<CALayer> m_contentsLayer; + + enum ContentsLayerPurpose { + NoContentsLayer = 0, + ContentsLayerForImage, + ContentsLayerForVideo + }; + + ContentsLayerPurpose m_contentsLayerPurpose; + bool m_contentsLayerHasBackgroundColor : 1; RetainPtr<WebAnimationDelegate> m_animationDelegate; - bool m_contentLayerForImageOrVideo; + RetainPtr<CGImageRef> m_pendingContentsImage; + + struct LayerAnimation { + LayerAnimation(CAPropertyAnimation* caAnim, const String& keyframesName, AnimatedPropertyID property, int index, double beginTime) + : m_animation(caAnim) + , m_keyframesName(keyframesName) + , m_property(property) + , m_index(index) + , m_beginTime(beginTime) + { } + + RetainPtr<CAPropertyAnimation*> m_animation; + String m_keyframesName; + AnimatedPropertyID m_property; + int m_index; + double m_beginTime; + }; + + Vector<LayerAnimation> m_uncomittedAnimations; + + // Animations on the layer are identified by property + index. + typedef int AnimatedProperty; // std containers choke on the AnimatedPropertyID enum + typedef pair<AnimatedProperty, int> AnimationPair; + + HashSet<AnimatedProperty> m_transitionPropertiesToRemove; + + enum { Remove, Pause }; + typedef int AnimationProcessingAction; + typedef HashMap<String, AnimationProcessingAction> AnimationsToProcessMap; + AnimationsToProcessMap m_keyframeAnimationsToProcess; + + // Map of keyframe names to their associated lists of animations for running animations, so we can remove/pause them. + typedef HashMap<String, Vector<AnimationPair> > KeyframeAnimationsMap; + KeyframeAnimationsMap m_runningKeyframeAnimations; + + Vector<FloatRect> m_dirtyRects; + + LayerChangeFlags m_uncommittedChanges; }; } // namespace WebCore diff --git a/WebCore/platform/graphics/mac/GraphicsLayerCA.mm b/WebCore/platform/graphics/mac/GraphicsLayerCA.mm index f361437..e5b9035 100644 --- a/WebCore/platform/graphics/mac/GraphicsLayerCA.mm +++ b/WebCore/platform/graphics/mac/GraphicsLayerCA.mm @@ -43,8 +43,10 @@ #import "TranslateTransformOperation.h" #import "WebLayer.h" #import "WebTiledLayer.h" +#import <limits.h> #import <wtf/CurrentTime.h> #import <wtf/UnusedParam.h> +#import <wtf/RetainPtr.h> using namespace std; @@ -80,8 +82,6 @@ static double mediaTimeToCurrentTime(CFTimeInterval t) } // namespace WebCore -static NSString* const WebAnimationCSSPropertyKey = @"GraphicsLayerCA_property"; - @interface WebAnimationDelegate : NSObject { WebCore::GraphicsLayerCA* m_graphicsLayer; } @@ -137,27 +137,25 @@ static inline void copyTransform(CATransform3D& toT3D, const TransformationMatri toT3D.m44 = narrowPrecisionToFloat(t.m44()); } -static NSValue* getTransformFunctionValue(const GraphicsLayer::TransformValue& transformValue, size_t index, const IntSize& size, TransformOperation::OperationType transformType) +static NSValue* getTransformFunctionValue(const TransformOperation* transformOp, TransformOperation::OperationType transformType, const IntSize& size) { - TransformOperation* op = (index >= transformValue.value()->operations().size()) ? 0 : transformValue.value()->operations()[index].get(); - switch (transformType) { case TransformOperation::ROTATE: case TransformOperation::ROTATE_X: case TransformOperation::ROTATE_Y: - return [NSNumber numberWithDouble:op ? deg2rad(static_cast<RotateTransformOperation*>(op)->angle()) : 0]; + return [NSNumber numberWithDouble:transformOp ? deg2rad(static_cast<const RotateTransformOperation*>(transformOp)->angle()) : 0]; case TransformOperation::SCALE_X: - return [NSNumber numberWithDouble:op ? static_cast<ScaleTransformOperation*>(op)->x() : 0]; + return [NSNumber numberWithDouble:transformOp ? static_cast<const ScaleTransformOperation*>(transformOp)->x() : 0]; case TransformOperation::SCALE_Y: - return [NSNumber numberWithDouble:op ? static_cast<ScaleTransformOperation*>(op)->y() : 0]; + return [NSNumber numberWithDouble:transformOp ? static_cast<const ScaleTransformOperation*>(transformOp)->y() : 0]; case TransformOperation::SCALE_Z: - return [NSNumber numberWithDouble:op ? static_cast<ScaleTransformOperation*>(op)->z() : 0]; + return [NSNumber numberWithDouble:transformOp ? static_cast<const ScaleTransformOperation*>(transformOp)->z() : 0]; case TransformOperation::TRANSLATE_X: - return [NSNumber numberWithDouble:op ? static_cast<TranslateTransformOperation*>(op)->x(size) : 0]; + return [NSNumber numberWithDouble:transformOp ? static_cast<const TranslateTransformOperation*>(transformOp)->x(size) : 0]; case TransformOperation::TRANSLATE_Y: - return [NSNumber numberWithDouble:op ? static_cast<TranslateTransformOperation*>(op)->y(size) : 0]; + return [NSNumber numberWithDouble:transformOp ? static_cast<const TranslateTransformOperation*>(transformOp)->y(size) : 0]; case TransformOperation::TRANSLATE_Z: - return [NSNumber numberWithDouble:op ? static_cast<TranslateTransformOperation*>(op)->z(size) : 0]; + return [NSNumber numberWithDouble:transformOp ? static_cast<const TranslateTransformOperation*>(transformOp)->z(size) : 0]; case TransformOperation::SCALE: case TransformOperation::TRANSLATE: case TransformOperation::SKEW_X: @@ -171,12 +169,12 @@ static NSValue* getTransformFunctionValue(const GraphicsLayer::TransformValue& t case TransformOperation::PERSPECTIVE: case TransformOperation::IDENTITY: case TransformOperation::NONE: { - TransformationMatrix t; - if (op) - op->apply(t, size); - CATransform3D cat; - copyTransform(cat, t); - return [NSValue valueWithCATransform3D:cat]; + TransformationMatrix transform; + if (transformOp) + transformOp->apply(transform, size); + CATransform3D caTransform; + copyTransform(caTransform, transform); + return [NSValue valueWithCATransform3D:caTransform]; } } @@ -212,6 +210,39 @@ static NSString* getValueFunctionNameForTransformOperation(TransformOperation::O } #endif +static String propertyIdToString(AnimatedPropertyID property) +{ + switch (property) { + case AnimatedPropertyWebkitTransform: + return "transform"; + case AnimatedPropertyOpacity: + return "opacity"; + case AnimatedPropertyBackgroundColor: + return "backgroundColor"; + case AnimatedPropertyInvalid: + ASSERT_NOT_REACHED(); + } + ASSERT_NOT_REACHED(); + return ""; +} + +static String animationIdentifier(AnimatedPropertyID property, int index) +{ + String animationId = propertyIdToString(property); + animationId.append("_"); + animationId.append(String::number(index)); + return animationId; +} + +#if !HAVE_MODERN_QUARTZCORE +static TransformationMatrix flipTransform() +{ + TransformationMatrix flipper; + flipper.flipY(); + return flipper; +} +#endif + static CAMediaTimingFunction* getCAMediaTimingFunction(const TimingFunction& timingFunction) { switch (timingFunction.type()) { @@ -250,15 +281,6 @@ static void clearLayerBackgroundColor(PlatformLayer* layer) [layer setBackgroundColor:0]; } -static CALayer* getPresentationLayer(CALayer* layer) -{ - CALayer* presLayer = [layer presentationLayer]; - if (!presLayer) - presLayer = layer; - - return presLayer; -} - static bool caValueFunctionSupported() { static bool sHaveValueFunction = [CAPropertyAnimation instancesRespondToSelector:@selector(setValueFunction:)]; @@ -304,9 +326,7 @@ static NSDictionary* nullActionsDictionary() nullValue, @"sublayerTransform", nullValue, @"sublayers", nullValue, @"transform", -#ifndef NDEBUG nullValue, @"zPosition", -#endif nil]; return actions; } @@ -318,12 +338,18 @@ GraphicsLayer* GraphicsLayer::createGraphicsLayer(GraphicsLayerClient* client) GraphicsLayerCA::GraphicsLayerCA(GraphicsLayerClient* client) : GraphicsLayer(client) -, m_contentLayerForImageOrVideo(false) +, m_contentsLayerPurpose(NoContentsLayer) +, m_contentsLayerHasBackgroundColor(false) +, m_uncommittedChanges(NoChange) { BEGIN_BLOCK_OBJC_EXCEPTIONS m_layer.adoptNS([[WebLayer alloc] init]); [m_layer.get() setLayerOwner:this]; +#if !HAVE_MODERN_QUARTZCORE + setContentsOrientation(defaultContentsOrientation()); +#endif + #ifndef NDEBUG updateDebugIndicators(); #endif @@ -336,21 +362,18 @@ GraphicsLayerCA::GraphicsLayerCA(GraphicsLayerClient* client) GraphicsLayerCA::~GraphicsLayerCA() { - // Remove a inner layer if there is one. - clearContents(); - + // We release our references to the CALayers here, but do not actively unparent them, + // since that will cause a commit and break our batched commit model. The layers will + // get released when the rootmost modified GraphicsLayerCA rebuilds its child layers. + 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]; @@ -361,10 +384,7 @@ 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 + noteLayerPropertyChanged(NameChanged); } NativeLayer GraphicsLayerCA::nativeLayer() const @@ -375,176 +395,485 @@ NativeLayer GraphicsLayerCA::nativeLayer() const 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 + noteLayerPropertyChanged(ChildrenChanged); } 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 + noteLayerPropertyChanged(ChildrenChanged); } void GraphicsLayerCA::addChildBelow(GraphicsLayer* childLayer, GraphicsLayer* sibling) { - // FIXME: share code with base class - ASSERT(childLayer != this); - childLayer->removeFromParent(); + GraphicsLayer::addChildBelow(childLayer, sibling); + noteLayerPropertyChanged(ChildrenChanged); +} - 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; - } +void GraphicsLayerCA::addChildAbove(GraphicsLayer* childLayer, GraphicsLayer* sibling) +{ + GraphicsLayer::addChildAbove(childLayer, sibling); + noteLayerPropertyChanged(ChildrenChanged); +} + +bool GraphicsLayerCA::replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild) +{ + if (GraphicsLayer::replaceChild(oldChild, newChild)) { + noteLayerPropertyChanged(ChildrenChanged); + return true; } - childLayer->setParent(this); + return false; +} - BEGIN_BLOCK_OBJC_EXCEPTIONS +void GraphicsLayerCA::removeFromParent() +{ + if (m_parent) + static_cast<GraphicsLayerCA*>(m_parent)->noteLayerPropertyChanged(ChildrenChanged); + GraphicsLayer::removeFromParent(); +} - 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()]; - } +void GraphicsLayerCA::setPosition(const FloatPoint& point) +{ + if (point == m_position) + return; - END_BLOCK_OBJC_EXCEPTIONS + GraphicsLayer::setPosition(point); + noteLayerPropertyChanged(PositionChanged); } -void GraphicsLayerCA::addChildAbove(GraphicsLayer* childLayer, GraphicsLayer* sibling) +void GraphicsLayerCA::setAnchorPoint(const FloatPoint3D& point) { - // FIXME: share code with base class - ASSERT(childLayer != this); - childLayer->removeFromParent(); + if (point == m_anchorPoint) + return; + + GraphicsLayer::setAnchorPoint(point); + noteLayerPropertyChanged(AnchorPointChanged); +} - 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; +void GraphicsLayerCA::setSize(const FloatSize& size) +{ + if (size == m_size) + return; + + GraphicsLayer::setSize(size); + noteLayerPropertyChanged(SizeChanged); +} + +void GraphicsLayerCA::setTransform(const TransformationMatrix& t) +{ + if (t == m_transform) + return; + + GraphicsLayer::setTransform(t); + noteLayerPropertyChanged(TransformChanged); +} + +void GraphicsLayerCA::setChildrenTransform(const TransformationMatrix& t) +{ + if (t == m_childrenTransform) + return; + + GraphicsLayer::setChildrenTransform(t); + noteLayerPropertyChanged(ChildrenTransformChanged); +} + +static void moveAnimation(AnimatedPropertyID property, CALayer* fromLayer, CALayer* toLayer) +{ + for (int index = 0; ; ++index) { + String animName = animationIdentifier(property, index); + + CAAnimation* anim = [fromLayer animationForKey:animName]; + if (!anim) break; - } + + [anim retain]; + [fromLayer removeAnimationForKey:animName]; + [toLayer addAnimation:anim forKey:animName]; + [anim release]; } - childLayer->setParent(this); +} - BEGIN_BLOCK_OBJC_EXCEPTIONS +void GraphicsLayerCA::setPreserves3D(bool preserves3D) +{ + if (preserves3D == m_preserves3D) + return; - 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()]; + GraphicsLayer::setPreserves3D(preserves3D); + noteLayerPropertyChanged(Preserves3DChanged); +} + +void GraphicsLayerCA::setMasksToBounds(bool masksToBounds) +{ + if (masksToBounds == m_masksToBounds) + return; + + GraphicsLayer::setMasksToBounds(masksToBounds); + noteLayerPropertyChanged(MasksToBoundsChanged); +} + +void GraphicsLayerCA::setDrawsContent(bool drawsContent) +{ + if (drawsContent == m_drawsContent) + return; + + GraphicsLayer::setDrawsContent(drawsContent); + noteLayerPropertyChanged(DrawsContentChanged); +} + +void GraphicsLayerCA::setBackgroundColor(const Color& color) +{ + if (m_backgroundColorSet && m_backgroundColor == color) + return; + + GraphicsLayer::setBackgroundColor(color); + + m_contentsLayerHasBackgroundColor = true; + noteLayerPropertyChanged(BackgroundColorChanged); +} + +void GraphicsLayerCA::clearBackgroundColor() +{ + if (!m_backgroundColorSet) + return; + + GraphicsLayer::clearBackgroundColor(); + m_contentsLayerHasBackgroundColor = false; + noteLayerPropertyChanged(BackgroundColorChanged); +} + +void GraphicsLayerCA::setContentsOpaque(bool opaque) +{ + if (m_contentsOpaque == opaque) + return; + + GraphicsLayer::setContentsOpaque(opaque); + noteLayerPropertyChanged(ContentsOpaqueChanged); +} + +void GraphicsLayerCA::setBackfaceVisibility(bool visible) +{ + if (m_backfaceVisibility == visible) + return; + + GraphicsLayer::setBackfaceVisibility(visible); + noteLayerPropertyChanged(BackfaceVisibilityChanged); +} + +void GraphicsLayerCA::setOpacity(float opacity) +{ + float clampedOpacity = max(0.0f, min(opacity, 1.0f)); + + if (clampedOpacity == m_opacity) + return; + + GraphicsLayer::setOpacity(clampedOpacity); + noteLayerPropertyChanged(OpacityChanged); +} + +void GraphicsLayerCA::setNeedsDisplay() +{ + FloatRect hugeRect(-numeric_limits<float>::max() / 2, -numeric_limits<float>::max() / 2, + numeric_limits<float>::max(), numeric_limits<float>::max()); + + setNeedsDisplayInRect(hugeRect); +} + +void GraphicsLayerCA::setNeedsDisplayInRect(const FloatRect& rect) +{ + if (!drawsContent()) + return; + + const size_t maxDirtyRects = 32; + + for (size_t i = 0; i < m_dirtyRects.size(); ++i) { + if (m_dirtyRects[i].contains(rect)) + return; } + + if (m_dirtyRects.size() < maxDirtyRects) + m_dirtyRects.append(rect); + else + m_dirtyRects[0].unite(rect); - END_BLOCK_OBJC_EXCEPTIONS + noteLayerPropertyChanged(DirtyRectsChanged); } -bool GraphicsLayerCA::replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild) +void GraphicsLayerCA::setContentsRect(const IntRect& rect) { - // FIXME: share code with base class - ASSERT(!newChild->parent()); + if (rect == m_contentsRect) + return; - bool found = false; - for (unsigned i = 0; i < m_children.size(); i++) { - if (oldChild == m_children[i]) { - m_children[i] = newChild; - found = true; - break; + GraphicsLayer::setContentsRect(rect); + noteLayerPropertyChanged(ContentsRectChanged); +} + +bool GraphicsLayerCA::addAnimation(const KeyframeValueList& valueList, const IntSize& boxSize, const Animation* anim, const String& keyframesName, double beginTime) +{ + if (forceSoftwareAnimation() || !anim || anim->isEmptyOrZeroDuration() || valueList.size() < 2) + return false; + +#if !HAVE_MODERN_QUARTZCORE + // Older versions of QuartzCore do not handle opacity in transform layers properly, so we will + // always do software animation in that case. + if (valueList.property() == AnimatedPropertyOpacity) + return false; +#endif + + bool createdAnimations = false; + if (valueList.property() == AnimatedPropertyWebkitTransform) + createdAnimations = createTransformAnimationsFromKeyframes(valueList, anim, keyframesName, beginTime, boxSize); + else + createdAnimations = createAnimationFromKeyframes(valueList, anim, keyframesName, beginTime); + + if (createdAnimations) + noteLayerPropertyChanged(AnimationChanged); + + return createdAnimations; +} + +void GraphicsLayerCA::removeAnimationsForProperty(AnimatedPropertyID property) +{ + if (m_transitionPropertiesToRemove.find(property) != m_transitionPropertiesToRemove.end()) + return; + + m_transitionPropertiesToRemove.add(property); + noteLayerPropertyChanged(AnimationChanged); +} + +void GraphicsLayerCA::removeAnimationsForKeyframes(const String& animationName) +{ + if (!animationIsRunning(animationName)) + return; + + m_keyframeAnimationsToProcess.add(animationName, Remove); + noteLayerPropertyChanged(AnimationChanged); +} + +void GraphicsLayerCA::pauseAnimation(const String& keyframesName) +{ + if (!animationIsRunning(keyframesName)) + return; + + AnimationsToProcessMap::iterator it = m_keyframeAnimationsToProcess.find(keyframesName); + if (it != m_keyframeAnimationsToProcess.end()) { + // If an animation is scheduled to be removed, don't change the remove to a pause. + if (it->second != Remove) + it->second = Pause; + } else + m_keyframeAnimationsToProcess.add(keyframesName, Pause); + + noteLayerPropertyChanged(AnimationChanged); +} + +void GraphicsLayerCA::setContentsToImage(Image* image) +{ + if (image) { + m_pendingContentsImage = image->nativeImageForCurrentFrame(); + CGColorSpaceRef colorSpace = CGImageGetColorSpace(m_pendingContentsImage.get()); + + static CGColorSpaceRef deviceRGB = CGColorSpaceCreateDeviceRGB(); + if (CFEqual(colorSpace, deviceRGB)) { + // CoreGraphics renders images tagged with DeviceRGB using GenericRGB. When we hand such + // images to CA we need to tag them similarly so CA rendering matches CG rendering. + static CGColorSpaceRef genericRGB = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); + m_pendingContentsImage.adoptCF(CGImageCreateCopyWithColorSpace(m_pendingContentsImage.get(), genericRGB)); } + m_contentsLayerPurpose = ContentsLayerForImage; + if (!m_contentsLayer) + noteLayerPropertyChanged(ChildrenChanged); + } else { + m_pendingContentsImage = 0; + m_contentsLayerPurpose = NoContentsLayer; + if (m_contentsLayer) + noteLayerPropertyChanged(ChildrenChanged); } - if (found) { - oldChild->setParent(0); + noteLayerPropertyChanged(ContentsImageChanged); +} + +void GraphicsLayerCA::setContentsToVideo(PlatformLayer* videoLayer) +{ + if (videoLayer != m_contentsLayer.get()) + noteLayerPropertyChanged(ChildrenChanged); - newChild->removeFromParent(); - newChild->setParent(this); + m_contentsLayer = videoLayer; + noteLayerPropertyChanged(ContentsVideoChanged); - 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; + m_contentsLayerPurpose = videoLayer ? ContentsLayerForVideo : NoContentsLayer; } -void GraphicsLayerCA::removeFromParent() +void GraphicsLayerCA::setGeometryOrientation(CompositingCoordinatesOrientation orientation) { - GraphicsLayer::removeFromParent(); + if (orientation == m_geometryOrientation) + return; - BEGIN_BLOCK_OBJC_EXCEPTIONS - [layerForSuperlayer() removeFromSuperlayer]; - END_BLOCK_OBJC_EXCEPTIONS + GraphicsLayer::setGeometryOrientation(orientation); + noteLayerPropertyChanged(GeometryOrientationChanged); + +#if !HAVE_MODERN_QUARTZCORE + // Geometry orientation is mapped onto children transform in older QuartzCores. + switch (m_geometryOrientation) { + case CompositingCoordinatesTopDown: + setChildrenTransform(TransformationMatrix()); + break; + + case CompositingCoordinatesBottomUp: + setChildrenTransform(flipTransform()); + break; + } +#endif } -void GraphicsLayerCA::setPosition(const FloatPoint& point) +void GraphicsLayerCA::syncCompositingState() { - // Don't short-circuit here, because position and anchor point are inter-dependent. - GraphicsLayer::setPosition(point); + recursiveCommitChanges(); +} - // 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::recursiveCommitChanges() +{ + commitLayerChanges(); + + const Vector<GraphicsLayer*>& childLayers = children(); + size_t numChildren = childLayers.size(); + for (size_t i = 0; i < numChildren; ++i) { + GraphicsLayerCA* curChild = static_cast<GraphicsLayerCA*>(childLayers[i]); + curChild->recursiveCommitChanges(); + } } -void GraphicsLayerCA::setAnchorPoint(const FloatPoint3D& point) +void GraphicsLayerCA::commitLayerChanges() { - // Don't short-circuit here, because position and anchor point are inter-dependent. - bool zChanged = (point.z() != m_anchorPoint.z()); - GraphicsLayer::setAnchorPoint(point); + if (!m_uncommittedChanges) + return; BEGIN_BLOCK_OBJC_EXCEPTIONS - // set the value on the layer to the new transform. - [primaryLayer() setAnchorPoint:FloatPoint(point.x(), point.y())]; - if (zChanged) { -#if HAVE_MODERN_QUARTZCORE - [primaryLayer() setAnchorPointZ:m_anchorPoint.z()]; -#endif + // Need to handle Preserves3DChanged first, because it affects which layers subsequent properties are applied to + if (m_uncommittedChanges & Preserves3DChanged) + updateLayerPreserves3D(); + + if (m_uncommittedChanges & NameChanged) { + if (m_transformLayer) + [m_transformLayer.get() setName:("Transform layer " + name())]; + [m_layer.get() setName:name()]; } - // Position depends on anchor point, so update it now. - setPosition(m_position); + if (m_uncommittedChanges & ContentsImageChanged) // Needs to happen before ChildrenChanged + updateContentsImage(); + + if (m_uncommittedChanges & ContentsVideoChanged) // Needs to happen before ChildrenChanged + updateContentsVideo(); + + if (m_uncommittedChanges & BackgroundColorChanged) // Needs to happen before ChildrenChanged, and after updating image or video + updateLayerBackgroundColor(); + + if (m_uncommittedChanges & ChildrenChanged) + updateSublayerList(); + + if (m_uncommittedChanges & PositionChanged) + updateLayerPosition(); + + if (m_uncommittedChanges & AnchorPointChanged) + updateAnchorPoint(); + + if (m_uncommittedChanges & SizeChanged) + updateLayerSize(); + + if (m_uncommittedChanges & TransformChanged) + updateTransform(); + + if (m_uncommittedChanges & ChildrenTransformChanged) + updateChildrenTransform(); + + if (m_uncommittedChanges & MasksToBoundsChanged) + updateMasksToBounds(); + + if (m_uncommittedChanges & DrawsContentChanged) + updateLayerDrawsContent(); + + if (m_uncommittedChanges & ContentsOpaqueChanged) + updateContentsOpaque(); + + if (m_uncommittedChanges & BackfaceVisibilityChanged) + updateBackfaceVisibility(); + + if (m_uncommittedChanges & OpacityChanged) + updateOpacityOnLayer(); + + if (m_uncommittedChanges & AnimationChanged) + updateLayerAnimations(); + + if (m_uncommittedChanges & DirtyRectsChanged) + repaintLayerDirtyRects(); + + if (m_uncommittedChanges & ContentsRectChanged) + updateContentsRect(); + + if (m_uncommittedChanges & GeometryOrientationChanged) + updateGeometryOrientation(); + + m_uncommittedChanges = NoChange; END_BLOCK_OBJC_EXCEPTIONS } -void GraphicsLayerCA::setSize(const FloatSize& size) +void GraphicsLayerCA::updateSublayerList() { - GraphicsLayer::setSize(size); + NSMutableArray* newSublayers = nil; - CGRect rect = CGRectMake(0.0f, - 0.0f, - m_size.width(), - m_size.height()); + if (m_transformLayer) { + // FIXME: add the primary layer in the correct order with negative z-order children. + newSublayers = [[NSMutableArray alloc] initWithObjects:m_layer.get(), nil]; + } else if (m_contentsLayer) { + // FIXME: add the contents layer in the correct order with negative z-order children. + newSublayers = [[NSMutableArray alloc] initWithObjects:m_contentsLayer.get(), nil]; + } - CGPoint centerPoint = CGPointMake(m_size.width() / 2.0f, m_size.height() / 2.0f); + const Vector<GraphicsLayer*>& childLayers = children(); + size_t numChildren = childLayers.size(); + for (size_t i = 0; i < numChildren; ++i) { + GraphicsLayerCA* curChild = static_cast<GraphicsLayerCA*>(childLayers[i]); + + CALayer* childLayer = curChild->layerForSuperlayer(); + if (!newSublayers) + newSublayers = [[NSMutableArray alloc] initWithObjects:childLayer, nil]; + else + [newSublayers addObject:childLayer]; + } + + [newSublayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)]; + + if (m_transformLayer) { + [m_transformLayer.get() setSublayers:newSublayers]; + + if (m_contentsLayer) { + // If we have a transform layer, then the contents layer is parented in the + // primary layer (which is itself a child of the transform layer). + [m_layer.get() setSublayers:nil]; + [m_layer.get() addSublayer:m_contentsLayer.get()]; + } + } else { + [m_layer.get() setSublayers:newSublayers]; + } + + [newSublayers release]; +} + +void GraphicsLayerCA::updateLayerPosition() +{ + // 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]; +} +void GraphicsLayerCA::updateLayerSize() +{ + CGRect rect = CGRectMake(0, 0, m_size.width(), m_size.height()); 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 + // The anchor of the contents layer is always at 0.5, 0.5, so the position is center-relative. + CGPoint centerPoint = CGPointMake(m_size.width() / 2.0f, m_size.height() / 2.0f); [m_layer.get() setPosition:centerPoint]; } @@ -553,136 +882,88 @@ void GraphicsLayerCA::setSize(const FloatSize& size) swapFromOrToTiledLayer(needTiledLayer); [m_layer.get() setBounds:rect]; + + // Contents transform may depend on height. + updateContentsTransform(); // Note that we don't resize m_contentsLayer. It's up the caller to do that. - 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); + // of the layer, taking anchor point into account. + updateLayerPosition(); } -void GraphicsLayerCA::setTransform(const TransformationMatrix& t) +void GraphicsLayerCA::updateAnchorPoint() +{ + [primaryLayer() setAnchorPoint:FloatPoint(m_anchorPoint.x(), m_anchorPoint.y())]; +#if HAVE_MODERN_QUARTZCORE + [primaryLayer() setAnchorPointZ:m_anchorPoint.z()]; +#endif + updateLayerPosition(); +} + +void GraphicsLayerCA::updateTransform() { - GraphicsLayer::setTransform(t); - - BEGIN_BLOCK_OBJC_EXCEPTIONS CATransform3D transform; - copyTransform(transform, t); + copyTransform(transform, m_transform); [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) +void GraphicsLayerCA::updateChildrenTransform() { - 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. + copyTransform(transform, m_childrenTransform); [primaryLayer() setSublayerTransform:transform]; - END_BLOCK_OBJC_EXCEPTIONS } -static void moveAnimation(AnimatedPropertyID property, CALayer* fromLayer, CALayer* toLayer) +void GraphicsLayerCA::updateMasksToBounds() { - 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]; - } + [m_layer.get() setMasksToBounds:m_masksToBounds]; +#ifndef NDEBUG + updateDebugIndicators(); +#endif } -static void moveSublayers(CALayer* fromLayer, CALayer* toLayer) +void GraphicsLayerCA::updateContentsOpaque() { - 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]; + [m_layer.get() setOpaque:m_contentsOpaque]; } -void GraphicsLayerCA::setPreserves3D(bool preserves3D) +void GraphicsLayerCA::updateBackfaceVisibility() { - 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 + [m_layer.get() setDoubleSided:m_backfaceVisibility]; +} +void GraphicsLayerCA::updateLayerPreserves3D() +{ Class transformLayerClass = NSClassFromString(@"CATransformLayer"); - if (preserves3D && !m_transformLayer && transformLayerClass) { + if (!transformLayerClass) + return; + + if (m_preserves3D && !m_transformLayer) { // Create the transform layer. m_transformLayer.adoptNS([[transformLayerClass alloc] init]); // Turn off default animations. [m_transformLayer.get() setStyle:[NSDictionary dictionaryWithObject:nullActionsDictionary() forKey:@"actions"]]; - + #ifndef NDEBUG [m_transformLayer.get() setName:[NSString stringWithFormat:@"Transform Layer CATransformLayer(%p) GraphicsLayer(%p)", m_transformLayer.get(), this]]; #endif // Copy the position from this layer. - [m_transformLayer.get() setBounds:[m_layer.get() bounds]]; - [m_transformLayer.get() setPosition:[m_layer.get() position]]; - [m_transformLayer.get() setAnchorPoint:[m_layer.get() anchorPoint]]; -#if HAVE_MODERN_QUARTZCORE - [m_transformLayer.get() setAnchorPointZ:[m_layer.get() anchorPointZ]]; -#endif - [m_transformLayer.get() setContentsRect:[m_layer.get() contentsRect]]; -#ifndef NDEBUG - [m_transformLayer.get() setZPosition:[m_layer.get() zPosition]]; -#endif + updateLayerPosition(); + updateLayerSize(); + updateAnchorPoint(); + updateTransform(); + updateChildrenTransform(); - // The contents layer is positioned at (0,0) relative to the transformLayer. + CGPoint point = CGPointMake(m_size.width() / 2.0f, m_size.height() / 2.0f); [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() setAnchorPoint:CGPointMake(0.5f, 0.5f)]; [m_layer.get() setTransform:CATransform3DIdentity]; - // Transfer the opacity from the old layer to the transform layer. - [m_transformLayer.get() setOpacity:m_opacity]; + // Set the old layer to opacity of 1. Further down we will set the opacity on the transform layer. [m_layer.get() setOpacity:1]; // Move this layer to be a child of the transform layer. @@ -690,600 +971,584 @@ void GraphicsLayerCA::setPreserves3D(bool preserves3D) [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) { + + updateSublayerList(); + } else if (!m_preserves3D && m_transformLayer) { // Relace the transformLayer in the parent with this layer. [m_layer.get() removeFromSuperlayer]; [[m_transformLayer.get() superlayer] replaceSublayer:m_transformLayer.get() with:m_layer.get()]; moveAnimation(AnimatedPropertyWebkitTransform, m_transformLayer.get(), m_layer.get()); - moveSublayers(m_transformLayer.get(), m_layer.get()); - - // Reset the layer position and transform. - [m_layer.get() setPosition:[m_transformLayer.get() position]]; - [m_layer.get() setAnchorPoint:[m_transformLayer.get() anchorPoint]]; -#if HAVE_MODERN_QUARTZCORE - [m_layer.get() setAnchorPointZ:[m_transformLayer.get() anchorPointZ]]; -#endif - [m_layer.get() setContentsRect:[m_transformLayer.get() contentsRect]]; - [m_layer.get() setTransform:[m_transformLayer.get() transform]]; - [m_layer.get() setOpacity:[m_transformLayer.get() opacity]]; -#ifndef NDEBUG - [m_layer.get() setZPosition:[m_transformLayer.get() zPosition]]; -#endif // Release the transform layer. m_transformLayer = 0; + + updateLayerPosition(); + updateLayerSize(); + updateAnchorPoint(); + updateTransform(); + updateChildrenTransform(); + + updateSublayerList(); } - END_BLOCK_OBJC_EXCEPTIONS + updateOpacityOnLayer(); } -void GraphicsLayerCA::setMasksToBounds(bool masksToBounds) +void GraphicsLayerCA::updateLayerDrawsContent() { - if (masksToBounds == m_masksToBounds) - return; - - GraphicsLayer::setMasksToBounds(masksToBounds); + bool needTiledLayer = requiresTiledLayer(m_size); + if (needTiledLayer != m_usingTiledLayer) + swapFromOrToTiledLayer(needTiledLayer); - BEGIN_BLOCK_OBJC_EXCEPTIONS - [m_layer.get() setMasksToBounds:masksToBounds]; - END_BLOCK_OBJC_EXCEPTIONS + if (m_drawsContent) + [m_layer.get() setNeedsDisplay]; + else + [m_layer.get() setContents:nil]; #ifndef NDEBUG updateDebugIndicators(); #endif } -void GraphicsLayerCA::setDrawsContent(bool drawsContent) +void GraphicsLayerCA::updateLayerBackgroundColor() { - if (drawsContent != m_drawsContent) { - GraphicsLayer::setDrawsContent(drawsContent); - - bool needTiledLayer = requiresTiledLayer(m_size); - if (needTiledLayer != m_usingTiledLayer) - swapFromOrToTiledLayer(needTiledLayer); + if (!m_contentsLayer) + return; - BEGIN_BLOCK_OBJC_EXCEPTIONS - if (m_drawsContent) - [m_layer.get() setNeedsDisplay]; - else - [m_layer.get() setContents:nil]; - -#ifndef NDEBUG - updateDebugIndicators(); -#endif - END_BLOCK_OBJC_EXCEPTIONS - } + // We never create the contents layer just for background color yet. + if (m_backgroundColorSet) + setLayerBackgroundColor(m_contentsLayer.get(), m_backgroundColor); + else + clearLayerBackgroundColor(m_contentsLayer.get()); } -void GraphicsLayerCA::setBackgroundColor(const Color& color, const Animation* transition, double beginTime) +void GraphicsLayerCA::updateContentsImage() { - GraphicsLayer::setBackgroundColor(color, transition, beginTime); - - BEGIN_BLOCK_OBJC_EXCEPTIONS - - if (!m_contentsLayer.get()) { - WebLayer* colorLayer = [WebLayer layer]; + if (m_pendingContentsImage) { + if (!m_contentsLayer.get()) { + WebLayer* imageLayer = [WebLayer layer]; #ifndef NDEBUG - [colorLayer setName:@"Color Layer"]; + [imageLayer setName:@"Image Layer"]; #endif - setContentsLayer(colorLayer); - } - - if (transition && !transition->isEmptyOrZeroDuration()) { - CALayer* presLayer = [m_contentsLayer.get() presentationLayer]; - // If we don't have a presentationLayer, just use the CALayer - if (!presLayer) - presLayer = m_contentsLayer.get(); - - // Get the current value of the background color from the layer - CGColorRef fromBackgroundColor = [presLayer backgroundColor]; - - CGColorRef bgColor = createCGColor(color); - setBasicAnimation(AnimatedPropertyBackgroundColor, TransformOperation::NONE, 0, fromBackgroundColor, bgColor, true, transition, beginTime); - CGColorRelease(bgColor); + setupContentsLayer(imageLayer); + m_contentsLayer.adoptNS([imageLayer retain]); + // m_contentsLayer will be parented by updateSublayerList + } + + // FIXME: maybe only do trilinear if the image is being scaled down, + // but then what if the layer size changes? +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + [m_contentsLayer.get() setMinificationFilter:kCAFilterTrilinear]; +#endif + [m_contentsLayer.get() setContents:(id)m_pendingContentsImage.get()]; + m_pendingContentsImage = 0; + + updateContentsRect(); } else { - removeAllAnimationsForProperty(AnimatedPropertyBackgroundColor); - setLayerBackgroundColor(m_contentsLayer.get(), m_backgroundColor); + // No image. + // m_contentsLayer will be removed via updateSublayerList. + m_contentsLayer = 0; } - - END_BLOCK_OBJC_EXCEPTIONS } -void GraphicsLayerCA::clearBackgroundColor() +void GraphicsLayerCA::updateContentsVideo() { - if (!m_contentLayerForImageOrVideo) - setContentsLayer(0); - else - clearLayerBackgroundColor(m_contentsLayer.get()); + // Video layer was set as m_contentsLayer, and will get parented in updateSublayerList(). + if (m_contentsLayer) { + setupContentsLayer(m_contentsLayer.get()); + updateContentsRect(); + } } -void GraphicsLayerCA::setContentsOpaque(bool opaque) +void GraphicsLayerCA::updateContentsRect() { - GraphicsLayer::setContentsOpaque(opaque); + if (!m_contentsLayer) + return; - BEGIN_BLOCK_OBJC_EXCEPTIONS - [m_layer.get() setOpaque:m_contentsOpaque]; - END_BLOCK_OBJC_EXCEPTIONS + CGPoint point = CGPointMake(m_contentsRect.x(), + m_contentsRect.y()); + CGRect rect = CGRectMake(0.0f, + 0.0f, + m_contentsRect.width(), + m_contentsRect.height()); + + [m_contentsLayer.get() setPosition:point]; + [m_contentsLayer.get() setBounds:rect]; } -void GraphicsLayerCA::setBackfaceVisibility(bool visible) +void GraphicsLayerCA::updateGeometryOrientation() { - if (m_backfaceVisibility == visible) - return; - - GraphicsLayer::setBackfaceVisibility(visible); - - BEGIN_BLOCK_OBJC_EXCEPTIONS - [m_layer.get() setDoubleSided:visible]; - END_BLOCK_OBJC_EXCEPTIONS +#if HAVE_MODERN_QUARTZCORE + switch (geometryOrientation()) { + case CompositingCoordinatesTopDown: + [m_layer.get() setGeometryFlipped:NO]; + break; + + case CompositingCoordinatesBottomUp: + [m_layer.get() setGeometryFlipped:YES]; + break; + } + // Geometry orientation is mapped onto children transform in older QuartzCores, + // so is handled via setGeometryOrientation(). +#endif } -bool GraphicsLayerCA::setOpacity(float opacity, const Animation* transition, double beginTime) +void GraphicsLayerCA::updateLayerAnimations() { - if (forceSoftwareAnimation()) - return false; - - float clampedOpacity = max(0.0f, min(opacity, 1.0f)); - - bool opacitiesDiffer = (m_opacity != clampedOpacity); - - GraphicsLayer::setOpacity(clampedOpacity, transition, beginTime); - - int animIndex = findAnimationEntry(AnimatedPropertyOpacity, 0); - - // If we don't have a transition just set the opacity - if (!transition || transition->isEmptyOrZeroDuration()) { - // Three cases: - // 1) no existing animation or transition: just set opacity - // 2) existing transition: clear it and set opacity - // 3) existing animation: just return - // - if (animIndex < 0 || m_animations[animIndex].isTransition()) { - BEGIN_BLOCK_OBJC_EXCEPTIONS - [primaryLayer() setOpacity:opacity]; - if (animIndex >= 0) { - removeAllAnimationsForProperty(AnimatedPropertyOpacity); - animIndex = -1; - } else { - String keyPath = propertyIdToString(AnimatedPropertyOpacity); - - // FIXME: using hardcoded '0' here. We should be clearing all animations. For now there is only 1. - String animName = keyPath + "_0"; - [animatedLayer(AnimatedPropertyOpacity) removeAnimationForKey:animName]; + if (m_transitionPropertiesToRemove.size()) { + HashSet<int>::const_iterator end = m_transitionPropertiesToRemove.end(); + for (HashSet<AnimatedProperty>::const_iterator it = m_transitionPropertiesToRemove.begin(); it != end; ++it) { + AnimatedPropertyID currProperty = static_cast<AnimatedPropertyID>(*it); + // Remove all animations with this property in the key. + // We can't tell if this property is animating via a transition or animation here, but + // that's OK because the style system never sends both transitions and animations for the same property. + for (int index = 0; ; ++index) { + if (!removeAnimationFromLayer(currProperty, index)) + break; } - 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; } + + m_transitionPropertiesToRemove.clear(); } - - // 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 + if (m_keyframeAnimationsToProcess.size()) { + AnimationsToProcessMap::const_iterator end = m_keyframeAnimationsToProcess.end(); + for (AnimationsToProcessMap::const_iterator it = m_keyframeAnimationsToProcess.begin(); it != end; ++it) { + const String& currKeyframeName = it->first; + KeyframeAnimationsMap::iterator animationIt = m_runningKeyframeAnimations.find(currKeyframeName); + if (animationIt == m_runningKeyframeAnimations.end()) + continue; + + AnimationProcessingAction action = it->second; + const Vector<AnimationPair>& animations = animationIt->second; + for (size_t i = 0; i < animations.size(); ++i) { + const AnimationPair& currPair = animations[i]; + switch (action) { + case Remove: + removeAnimationFromLayer(static_cast<AnimatedPropertyID>(currPair.first), currPair.second); + break; + case Pause: + pauseAnimationOnLayer(static_cast<AnimatedPropertyID>(currPair.first), currPair.second); + break; + } + } + + if (action == Remove) + m_runningKeyframeAnimations.remove(currKeyframeName); + } - NSNumber* fromOpacityValue = nil; - NSNumber* toOpacityValue = [NSNumber numberWithFloat:opacity]; + m_keyframeAnimationsToProcess.clear(); + } - if (transition && !transition->isEmptyOrZeroDuration()) { - CALayer* presLayer = getPresentationLayer(primaryLayer()); - float fromOpacity = [presLayer opacity]; - fromOpacityValue = [NSNumber numberWithFloat:fromOpacity]; - setBasicAnimation(AnimatedPropertyOpacity, TransformOperation::NONE, 0, fromOpacityValue, toOpacityValue, true, transition, beginTime); - didAnimate = true; + size_t numAnimations; + if ((numAnimations = m_uncomittedAnimations.size())) { + for (size_t i = 0; i < numAnimations; ++i) { + const LayerAnimation& pendingAnimation = m_uncomittedAnimations[i]; + setAnimationOnLayer(pendingAnimation.m_animation.get(), pendingAnimation.m_property, pendingAnimation.m_index, pendingAnimation.m_beginTime); + + if (!pendingAnimation.m_keyframesName.isEmpty()) { + // If this is a keyframe anim, we have to remember the association of keyframes name to property/index pairs, + // so we can remove the animations later if needed. + // For transitions, we can just generate animation names with property and index. + KeyframeAnimationsMap::iterator it = m_runningKeyframeAnimations.find(pendingAnimation.m_keyframesName); + if (it == m_runningKeyframeAnimations.end()) { + Vector<AnimationPair> firstPair; + firstPair.append(AnimationPair(pendingAnimation.m_property, pendingAnimation.m_index)); + m_runningKeyframeAnimations.add(pendingAnimation.m_keyframesName, firstPair); + } else { + Vector<AnimationPair>& animPairs = it->second; + animPairs.append(AnimationPair(pendingAnimation.m_property, pendingAnimation.m_index)); + } + } + } + + m_uncomittedAnimations.clear(); } +} - END_BLOCK_OBJC_EXCEPTIONS +void GraphicsLayerCA::setAnimationOnLayer(CAPropertyAnimation* caAnim, AnimatedPropertyID property, int index, double beginTime) +{ + PlatformLayer* layer = animatedLayer(property); + + if (beginTime) { + NSTimeInterval time = [layer convertTime:currentTimeToMediaTime(beginTime) fromLayer:nil]; + [caAnim setBeginTime:time]; + } - return didAnimate; + String animationName = animationIdentifier(property, index); + + [layer removeAnimationForKey:animationName]; + [layer addAnimation:caAnim forKey:animationName]; } -void GraphicsLayerCA::setNeedsDisplay() +bool GraphicsLayerCA::removeAnimationFromLayer(AnimatedPropertyID property, int index) { - BEGIN_BLOCK_OBJC_EXCEPTIONS + PlatformLayer* layer = animatedLayer(property); + + String animationName = animationIdentifier(property, index); + + if (![layer animationForKey:animationName]) + return false; - if (drawsContent()) - [m_layer.get() setNeedsDisplay]; + [layer removeAnimationForKey:animationName]; + return true; +} - END_BLOCK_OBJC_EXCEPTIONS + +static void copyAnimationProperties(CAPropertyAnimation* from, CAPropertyAnimation* to) +{ + [to setBeginTime:[from beginTime]]; + [to setDuration:[from duration]]; + [to setRepeatCount:[from repeatCount]]; + [to setAutoreverses:[from autoreverses]]; + [to setFillMode:[from fillMode]]; + [to setRemovedOnCompletion:[from isRemovedOnCompletion]]; + [to setAdditive:[from isAdditive]]; + [to setTimingFunction:[from timingFunction]]; + +#if HAVE_MODERN_QUARTZCORE + [to setValueFunction:[from valueFunction]]; +#endif } -void GraphicsLayerCA::setNeedsDisplayInRect(const FloatRect& rect) +void GraphicsLayerCA::pauseAnimationOnLayer(AnimatedPropertyID property, int index) { - BEGIN_BLOCK_OBJC_EXCEPTIONS - - if (drawsContent()) - [m_layer.get() setNeedsDisplayInRect:rect]; + PlatformLayer* layer = animatedLayer(property); - END_BLOCK_OBJC_EXCEPTIONS + String animationName = animationIdentifier(property, index); + + CAAnimation* caAnim = [layer animationForKey:animationName]; + if (!caAnim) + return; + + // Animations on the layer are immutable, so we have to clone and modify. + CAPropertyAnimation* pausedAnim = nil; + if ([caAnim isKindOfClass:[CAKeyframeAnimation class]]) { + CAKeyframeAnimation* existingKeyframeAnim = static_cast<CAKeyframeAnimation*>(caAnim); + CAKeyframeAnimation* newAnim = [CAKeyframeAnimation animationWithKeyPath:[existingKeyframeAnim keyPath]]; + copyAnimationProperties(existingKeyframeAnim, newAnim); + [newAnim setValues:[existingKeyframeAnim values]]; + [newAnim setKeyTimes:[existingKeyframeAnim keyTimes]]; + [newAnim setTimingFunctions:[existingKeyframeAnim timingFunctions]]; + pausedAnim = newAnim; + } else if ([caAnim isKindOfClass:[CABasicAnimation class]]) { + CABasicAnimation* existingPropertyAnim = static_cast<CABasicAnimation*>(caAnim); + CABasicAnimation* newAnim = [CABasicAnimation animationWithKeyPath:[existingPropertyAnim keyPath]]; + copyAnimationProperties(existingPropertyAnim, newAnim); + [newAnim setFromValue:[existingPropertyAnim fromValue]]; + [newAnim setToValue:[existingPropertyAnim toValue]]; + pausedAnim = newAnim; + } + + double t = [layer convertTime:currentTimeToMediaTime(currentTime()) fromLayer:nil]; + [pausedAnim setSpeed:0]; + [pausedAnim setTimeOffset:t - [caAnim beginTime]]; + [layer addAnimation:pausedAnim forKey:animationName]; // This will replace the running animation. } +void GraphicsLayerCA::repaintLayerDirtyRects() +{ + if (!m_dirtyRects.size()) + return; + + for (size_t i = 0; i < m_dirtyRects.size(); ++i) + [m_layer.get() setNeedsDisplayInRect:m_dirtyRects[i]]; + + m_dirtyRects.clear(); +} -bool GraphicsLayerCA::animateTransform(const TransformValueList& valueList, const IntSize& size, const Animation* anim, double beginTime, bool isTransition) +bool GraphicsLayerCA::createAnimationFromKeyframes(const KeyframeValueList& valueList, const Animation* animation, const String& keyframesName, double beginTime) { - if (forceSoftwareAnimation() || !anim || anim->isEmptyOrZeroDuration() || valueList.size() < 2) + ASSERT(valueList.property() != AnimatedPropertyWebkitTransform); + + BEGIN_BLOCK_OBJC_EXCEPTIONS; + + bool isKeyframe = valueList.size() > 2; + bool valuesOK; + + bool additive = false; + int animationIndex = 0; + + CAPropertyAnimation* caAnimation; + if (isKeyframe) { + CAKeyframeAnimation* keyframeAnim = createKeyframeAnimation(animation, valueList.property(), additive); + valuesOK = setAnimationKeyframes(valueList, animation, keyframeAnim); + caAnimation = keyframeAnim; + } else { + CABasicAnimation* basicAnim = createBasicAnimation(animation, valueList.property(), additive); + valuesOK = setAnimationEndpoints(valueList, animation, basicAnim); + caAnimation = basicAnim; + } + + if (!valuesOK) return false; - - TransformValueList::FunctionList functionList; - bool isValid, hasBigRotation; - valueList.makeFunctionList(functionList, isValid, hasBigRotation); + + m_uncomittedAnimations.append(LayerAnimation(caAnimation, keyframesName, valueList.property(), animationIndex, beginTime)); + + END_BLOCK_OBJC_EXCEPTIONS; + + return true; +} + +bool GraphicsLayerCA::createTransformAnimationsFromKeyframes(const KeyframeValueList& valueList, const Animation* animation, const String& keyframesName, double beginTime, const IntSize& boxSize) +{ + ASSERT(valueList.property() == AnimatedPropertyWebkitTransform); + + TransformOperationList functionList; + bool listsMatch, hasBigRotation; + fetchTransformOperationList(valueList, functionList, listsMatch, hasBigRotation); // We need to fall back to software animation if we don't have setValueFunction:, and // we would need to animate each incoming transform function separately. This is the // case if we have a rotation >= 180 or we have more than one transform function. if ((hasBigRotation || functionList.size() > 1) && !caValueFunctionSupported()) return false; + + bool validMatrices = true; + + BEGIN_BLOCK_OBJC_EXCEPTIONS; + + // If functionLists don't match we do a matrix animation, otherwise we do a component hardware animation. + // Also, we can't do component animation unless we have valueFunction, so we need to do matrix animation + // if that's not true as well. + bool isMatrixAnimation = !listsMatch || !caValueFunctionSupported(); - 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]; - + size_t numAnimations = isMatrixAnimation ? 1 : functionList.size(); 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 = isMatrixAnimation ? TransformOperation::MATRIX_3D : functionList[functionIndex]; + for (size_t animationIndex = 0; animationIndex < numAnimations; ++animationIndex) { + TransformOperation::OperationType transformOp = isMatrixAnimation ? TransformOperation::MATRIX_3D : functionList[animationIndex]; + CAPropertyAnimation* caAnimation; + // CA applies animations in reverse order (<rdar://problem/7095638>) so we need the last one we add (per property) + // to be non-additive. + bool additive = animationIndex < (numAnimations - 1); 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, opType, functionIndex, timesArray, valArray, tfArray, isTransition, anim, beginTime); - - [timesArray release]; - [valArray release]; - [tfArray release]; + CAKeyframeAnimation* keyframeAnim = createKeyframeAnimation(animation, valueList.property(), additive); + validMatrices = setTransformAnimationKeyframes(valueList, animation, keyframeAnim, animationIndex, transformOp, isMatrixAnimation, boxSize); + caAnimation = keyframeAnim; } else { - // Is a transition - id fromValue, toValue; - - if (isMatrixAnimation) { - TransformationMatrix fromt, tot; - valueList.at(0).value()->apply(size, fromt); - valueList.at(1).value()->apply(size, tot); - - CATransform3D cat; - copyTransform(cat, fromt); - fromValue = [NSValue valueWithCATransform3D:cat]; - copyTransform(cat, tot); - toValue = [NSValue valueWithCATransform3D:cat]; - } else { - fromValue = getTransformFunctionValue(valueList.at(0), functionIndex, size, opType); - toValue = getTransformFunctionValue(valueList.at(1), functionIndex, size, opType); - } - - setBasicAnimation(AnimatedPropertyWebkitTransform, opType, functionIndex, fromValue, toValue, isTransition, anim, beginTime); + CABasicAnimation* basicAnim = createBasicAnimation(animation, valueList.property(), additive); + validMatrices = setTransformAnimationEndpoints(valueList, animation, basicAnim, animationIndex, transformOp, isMatrixAnimation, boxSize); + caAnimation = basicAnim; } - if (isMatrixAnimation) + if (!validMatrices) break; + + m_uncomittedAnimations.append(LayerAnimation(caAnimation, keyframesName, valueList.property(), animationIndex, beginTime)); } - END_BLOCK_OBJC_EXCEPTIONS - return true; + END_BLOCK_OBJC_EXCEPTIONS; + + return validMatrices; } -bool GraphicsLayerCA::animateFloat(AnimatedPropertyID property, const FloatValueList& valueList, const Animation* animation, double beginTime) +CABasicAnimation* GraphicsLayerCA::createBasicAnimation(const Animation* anim, AnimatedPropertyID property, bool additive) { - if (forceSoftwareAnimation() || valueList.size() < 2) - return false; - - // if there is already is an animation for this property and it hasn't changed, ignore it. - int i = findAnimationEntry(property, 0); - if (i >= 0 && *m_animations[i].animation() == *animation) { - m_animations[i].setIsCurrent(); - return false; - } - - if (valueList.size() == 2) { - float fromVal = valueList.at(0).value(); - float toVal = valueList.at(1).value(); - if (isnan(toVal) && isnan(fromVal)) - return false; - - // initialize the property to 0 - [animatedLayer(property) setValue:0 forKeyPath:propertyIdToString(property)]; - setBasicAnimation(property, TransformOperation::NONE, 0, isnan(fromVal) ? nil : [NSNumber numberWithFloat:fromVal], isnan(toVal) ? nil : [NSNumber numberWithFloat:toVal], false, animation, beginTime); - return true; - } - - BEGIN_BLOCK_OBJC_EXCEPTIONS - - NSMutableArray* timesArray = [[NSMutableArray alloc] init]; - NSMutableArray* valArray = [[NSMutableArray alloc] init]; - NSMutableArray* tfArray = [[NSMutableArray alloc] init]; - - for (unsigned i = 0; i < valueList.values().size(); ++i) { - const FloatValue& curValue = valueList.values()[i]; - [timesArray addObject:[NSNumber numberWithFloat:curValue.key()]]; - [valArray addObject:[NSNumber numberWithFloat:curValue.value()]]; - - const TimingFunction* tf = 0; - if (curValue.timingFunction()) - tf = curValue.timingFunction(); - else if (animation->isTimingFunctionSet()) - tf = &animation->timingFunction(); - - CAMediaTimingFunction* timingFunction = getCAMediaTimingFunction(tf ? *tf : TimingFunction()); - [tfArray addObject:timingFunction]; - } - - // We toss the last tfArray value because it has to one shorter than the others. - [tfArray removeLastObject]; - - // Initialize the property to 0. - [animatedLayer(property) setValue:0 forKeyPath:propertyIdToString(property)]; - // Then set the animation. - setKeyframeAnimation(property, TransformOperation::NONE, 0, timesArray, valArray, tfArray, false, animation, beginTime); - - [timesArray release]; - [valArray release]; - [tfArray release]; + CABasicAnimation* basicAnim = [CABasicAnimation animationWithKeyPath:propertyIdToString(property)]; + setupAnimation(basicAnim, anim, additive); + return basicAnim; +} - END_BLOCK_OBJC_EXCEPTIONS - return true; +CAKeyframeAnimation* GraphicsLayerCA::createKeyframeAnimation(const Animation* anim, AnimatedPropertyID property, bool additive) +{ + CAKeyframeAnimation* keyframeAnim = [CAKeyframeAnimation animationWithKeyPath:propertyIdToString(property)]; + setupAnimation(keyframeAnim, anim, additive); + return keyframeAnim; } -void GraphicsLayerCA::setContentsToImage(Image* image) +void GraphicsLayerCA::setupAnimation(CAPropertyAnimation* propertyAnim, const Animation* anim, bool additive) { - if (image) { - BEGIN_BLOCK_OBJC_EXCEPTIONS - { - if (!m_contentsLayer.get()) { - WebLayer* imageLayer = [WebLayer layer]; -#ifndef NDEBUG - [imageLayer setName:@"Image Layer"]; -#endif - setContentsLayer(imageLayer); - } + double duration = anim->duration(); + if (duration <= 0) + duration = cAnimationAlmostZeroDuration; - // FIXME: maybe only do trilinear if the image is being scaled down, - // but then what if the layer size changes? -#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) - [m_contentsLayer.get() setMinificationFilter:kCAFilterTrilinear]; -#endif - CGImageRef theImage = image->nativeImageForCurrentFrame(); - [m_contentsLayer.get() setContents:(id)theImage]; - } - END_BLOCK_OBJC_EXCEPTIONS - } else - setContentsLayer(0); + float repeatCount = anim->iterationCount(); + if (repeatCount == Animation::IterationCountInfinite) + repeatCount = FLT_MAX; + else if (anim->direction() == Animation::AnimationDirectionAlternate) + repeatCount /= 2; - m_contentLayerForImageOrVideo = (image != 0); -} + [propertyAnim setDuration:duration]; + [propertyAnim setRepeatCount:repeatCount]; + [propertyAnim setAutoreverses:anim->direction()]; + [propertyAnim setRemovedOnCompletion:NO]; + [propertyAnim setAdditive:additive]; + [propertyAnim setFillMode:@"extended"]; -void GraphicsLayerCA::setContentsToVideo(PlatformLayer* videoLayer) -{ - setContentsLayer(videoLayer); - m_contentLayerForImageOrVideo = (videoLayer != 0); + [propertyAnim setDelegate:m_animationDelegate.get()]; } -void GraphicsLayerCA::clearContents() +CAMediaTimingFunction* GraphicsLayerCA::timingFunctionForAnimationValue(const AnimationValue* animValue, const Animation* anim) { - if (m_contentLayerForImageOrVideo) { - setContentsLayer(0); - m_contentLayerForImageOrVideo = false; - } + const TimingFunction* tf = 0; + if (animValue->timingFunction()) + tf = animValue->timingFunction(); + else if (anim->isTimingFunctionSet()) + tf = &anim->timingFunction(); + + return getCAMediaTimingFunction(tf ? *tf : TimingFunction()); } -void GraphicsLayerCA::updateContentsRect() +bool GraphicsLayerCA::setAnimationEndpoints(const KeyframeValueList& valueList, const Animation* anim, CABasicAnimation* basicAnim) { - 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 + id fromValue = nil; + id toValue = nil; + + switch (valueList.property()) { + case AnimatedPropertyOpacity: { + const FloatAnimationValue* startVal = static_cast<const FloatAnimationValue*>(valueList.at(0)); + const FloatAnimationValue* endVal = static_cast<const FloatAnimationValue*>(valueList.at(1)); + fromValue = [NSNumber numberWithFloat:startVal->value()]; + toValue = [NSNumber numberWithFloat:endVal->value()]; + break; + } + default: + ASSERT_NOT_REACHED(); // we don't animate color yet + break; } + + // This codepath is used for 2-keyframe animations, so we still need to look in the start + // for a timing function. + CAMediaTimingFunction* timingFunction = timingFunctionForAnimationValue(valueList.at(0), anim); + [basicAnim setTimingFunction:timingFunction]; + + [basicAnim setFromValue:fromValue]; + [basicAnim setToValue:toValue]; + + return true; } -void GraphicsLayerCA::setBasicAnimation(AnimatedPropertyID property, TransformOperation::OperationType operationType, short index, void* fromVal, void* toVal, bool isTransition, const Animation* transition, double beginTime) +bool GraphicsLayerCA::setAnimationKeyframes(const KeyframeValueList& valueList, const Animation* anim, CAKeyframeAnimation* keyframeAnim) { - ASSERT(fromVal || toVal); + RetainPtr<NSMutableArray> keyTimes(AdoptNS, [[NSMutableArray alloc] init]); + RetainPtr<NSMutableArray> values(AdoptNS, [[NSMutableArray alloc] init]); + RetainPtr<NSMutableArray> timingFunctions(AdoptNS, [[NSMutableArray alloc] init]); - WebLayer* layer = animatedLayer(property); - - BEGIN_BLOCK_OBJC_EXCEPTIONS - - // add an entry for this animation - addAnimationEntry(property, index, isTransition, transition); + for (unsigned i = 0; i < valueList.size(); ++i) { + const AnimationValue* curValue = valueList.at(i); + [keyTimes.get() addObject:[NSNumber numberWithFloat:curValue->keyTime()]]; - String keyPath = propertyIdToString(property); - String animName = keyPath + "_" + String::number(index); + switch (valueList.property()) { + case AnimatedPropertyOpacity: { + const FloatAnimationValue* floatValue = static_cast<const FloatAnimationValue*>(curValue); + [values.get() addObject:[NSNumber numberWithFloat:floatValue->value()]]; + break; + } + default: + ASSERT_NOT_REACHED(); // we don't animate color yet + break; + } - CABasicAnimation* basicAnim = [CABasicAnimation animationWithKeyPath:keyPath]; + CAMediaTimingFunction* timingFunction = timingFunctionForAnimationValue(curValue, anim); + [timingFunctions.get() addObject:timingFunction]; + } - double duration = transition->duration(); - if (duration <= 0) - duration = cAnimationAlmostZeroDuration; - - float repeatCount = transition->iterationCount(); - if (repeatCount == Animation::IterationCountInfinite) - repeatCount = FLT_MAX; - else if (transition->direction() == Animation::AnimationDirectionAlternate) - repeatCount /= 2; - - [basicAnim setDuration:duration]; - [basicAnim setRepeatCount:repeatCount]; - [basicAnim setAutoreverses:transition->direction()]; - [basicAnim setRemovedOnCompletion:NO]; - - // Note that currently transform is the only property which has animations - // with an index > 0. - [basicAnim setAdditive:property == AnimatedPropertyWebkitTransform]; - [basicAnim setFillMode:@"extended"]; -#if HAVE_MODERN_QUARTZCORE - if (NSString* valueFunctionName = getValueFunctionNameForTransformOperation(operationType)) - [basicAnim setValueFunction:[CAValueFunction functionWithName:valueFunctionName]]; -#else - UNUSED_PARAM(operationType); -#endif + // We toss the last tfArray value because it has to one shorter than the others. + [timingFunctions.get() removeLastObject]; + + [keyframeAnim setKeyTimes:keyTimes.get()]; + [keyframeAnim setValues:values.get()]; + [keyframeAnim setTimingFunctions:timingFunctions.get()]; - // Set the delegate (and property value). - int prop = isTransition ? property : AnimatedPropertyInvalid; - [basicAnim setValue:[NSNumber numberWithInt:prop] forKey:WebAnimationCSSPropertyKey]; - [basicAnim setDelegate:m_animationDelegate.get()]; + return true; +} - NSTimeInterval bt = beginTime ? [layer convertTime:currentTimeToMediaTime(beginTime) fromLayer:nil] : 0; - [basicAnim setBeginTime:bt]; +bool GraphicsLayerCA::setTransformAnimationEndpoints(const KeyframeValueList& valueList, const Animation* anim, CABasicAnimation* basicAnim, int functionIndex, TransformOperation::OperationType transformOp, bool isMatrixAnimation, const IntSize& boxSize) +{ + id fromValue; + id toValue; + + ASSERT(valueList.size() == 2); + const TransformAnimationValue* startValue = static_cast<const TransformAnimationValue*>(valueList.at(0)); + const TransformAnimationValue* endValue = static_cast<const TransformAnimationValue*>(valueList.at(1)); - if (fromVal) - [basicAnim setFromValue:reinterpret_cast<id>(fromVal)]; - if (toVal) - [basicAnim setToValue:reinterpret_cast<id>(toVal)]; + if (isMatrixAnimation) { + TransformationMatrix fromTransform, toTransform; + startValue->value()->apply(boxSize, fromTransform); + endValue->value()->apply(boxSize, toTransform); - const TimingFunction* tf = 0; - if (transition->isTimingFunctionSet()) - tf = &transition->timingFunction(); + // If any matrix is singular, CA won't animate it correctly. So fall back to software animation + if (!fromTransform.isInvertible() || !toTransform.isInvertible()) + return false; + + CATransform3D caTransform; + copyTransform(caTransform, fromTransform); + fromValue = [NSValue valueWithCATransform3D:caTransform]; - CAMediaTimingFunction* timingFunction = getCAMediaTimingFunction(tf ? *tf : TimingFunction()); + copyTransform(caTransform, toTransform); + toValue = [NSValue valueWithCATransform3D:caTransform]; + } else { + fromValue = getTransformFunctionValue(startValue->value()->at(functionIndex), transformOp, boxSize); + toValue = getTransformFunctionValue(endValue->value()->at(functionIndex), transformOp, boxSize); + } + + // This codepath is used for 2-keyframe animations, so we still need to look in the start + // for a timing function. + CAMediaTimingFunction* timingFunction = timingFunctionForAnimationValue(valueList.at(0), anim); [basicAnim setTimingFunction:timingFunction]; - // Send over the animation. - [layer removeAnimationForKey:animName]; - [layer addAnimation:basicAnim forKey:animName]; - - END_BLOCK_OBJC_EXCEPTIONS + [basicAnim setFromValue:fromValue]; + [basicAnim setToValue:toValue]; + +#if HAVE_MODERN_QUARTZCORE + if (NSString* valueFunctionName = getValueFunctionNameForTransformOperation(transformOp)) + [basicAnim setValueFunction:[CAValueFunction functionWithName:valueFunctionName]]; +#endif + + return true; } -void GraphicsLayerCA::setKeyframeAnimation(AnimatedPropertyID property, TransformOperation::OperationType operationType, short index, void* keys, void* values, void* timingFunctions, - bool isTransition, const Animation* anim, double beginTime) +bool GraphicsLayerCA::setTransformAnimationKeyframes(const KeyframeValueList& valueList, const Animation* animation, CAKeyframeAnimation* keyframeAnim, int functionIndex, TransformOperation::OperationType transformOpType, bool isMatrixAnimation, const IntSize& boxSize) { - PlatformLayer* layer = animatedLayer(property); - - // Add an entry for this animation (which may change beginTime). - addAnimationEntry(property, index, isTransition, anim); + RetainPtr<NSMutableArray> keyTimes(AdoptNS, [[NSMutableArray alloc] init]); + RetainPtr<NSMutableArray> values(AdoptNS, [[NSMutableArray alloc] init]); + RetainPtr<NSMutableArray> timingFunctions(AdoptNS, [[NSMutableArray alloc] init]); - String keyPath = propertyIdToString(property); - String animName = keyPath + "_" + String::number(index); + for (unsigned i = 0; i < valueList.size(); ++i) { + const TransformAnimationValue* curValue = static_cast<const TransformAnimationValue*>(valueList.at(i)); + [keyTimes.get() addObject:[NSNumber numberWithFloat:curValue->keyTime()]]; - BEGIN_BLOCK_OBJC_EXCEPTIONS + if (isMatrixAnimation) { + TransformationMatrix transform; + curValue->value()->apply(boxSize, transform); - CAKeyframeAnimation* keyframeAnim = [CAKeyframeAnimation animationWithKeyPath:keyPath]; + // If any matrix is singular, CA won't animate it correctly. So fall back to software animation + if (!transform.isInvertible()) + return false; - double duration = anim->duration(); - if (duration <= 0) - duration = cAnimationAlmostZeroDuration; + CATransform3D caTransform; + copyTransform(caTransform, transform); + [values.get() addObject:[NSValue valueWithCATransform3D:caTransform]]; + } else { + const TransformOperation* transformOp = curValue->value()->at(functionIndex); + [values.get() addObject:getTransformFunctionValue(transformOp, transformOpType, boxSize)]; + } - float repeatCount = anim->iterationCount(); - if (repeatCount == Animation::IterationCountInfinite) - repeatCount = FLT_MAX; - else if (anim->direction() == Animation::AnimationDirectionAlternate) - repeatCount /= 2; + CAMediaTimingFunction* timingFunction = timingFunctionForAnimationValue(curValue, animation); + [timingFunctions.get() addObject:timingFunction]; + } + + // We toss the last tfArray value because it has to one shorter than the others. + [timingFunctions.get() removeLastObject]; - [keyframeAnim setDuration:duration]; - [keyframeAnim setRepeatCount:repeatCount]; - [keyframeAnim setAutoreverses:anim->direction()]; - [keyframeAnim setRemovedOnCompletion:NO]; + [keyframeAnim setKeyTimes:keyTimes.get()]; + [keyframeAnim setValues:values.get()]; + [keyframeAnim setTimingFunctions:timingFunctions.get()]; - // The first animation is non-additive, all the rest are additive. - // Note that currently transform is the only property which has animations - // with an index > 0. - [keyframeAnim setAdditive:(property == AnimatedPropertyWebkitTransform) ? YES : NO]; - [keyframeAnim setFillMode:@"extended"]; #if HAVE_MODERN_QUARTZCORE - if (NSString* valueFunctionName = getValueFunctionNameForTransformOperation(operationType)) + if (NSString* valueFunctionName = getValueFunctionNameForTransformOperation(transformOpType)) [keyframeAnim setValueFunction:[CAValueFunction functionWithName:valueFunctionName]]; -#else - UNUSED_PARAM(operationType); #endif - - [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 + return true; } -void GraphicsLayerCA::suspendAnimations() +void GraphicsLayerCA::suspendAnimations(double time) { - double t = currentTimeToMediaTime(currentTime()); + double t = currentTimeToMediaTime(time ? time : currentTime()); [primaryLayer() setSpeed:0]; [primaryLayer() setTimeOffset:t]; } @@ -1294,64 +1559,12 @@ void GraphicsLayerCA::resumeAnimations() [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 +WebLayer* GraphicsLayerCA::hostLayerForSublayers() const { return m_transformLayer ? m_transformLayer.get() : m_layer.get(); } -PlatformLayer* GraphicsLayerCA::layerForSuperlayer() const +WebLayer* GraphicsLayerCA::layerForSuperlayer() const { if (m_transformLayer) return m_transformLayer.get(); @@ -1359,6 +1572,11 @@ PlatformLayer* GraphicsLayerCA::layerForSuperlayer() const return m_layer.get(); } +CALayer* GraphicsLayerCA::animatedLayer(AnimatedPropertyID property) const +{ + return (property == AnimatedPropertyBackgroundColor) ? m_contentsLayer.get() : primaryLayer(); +} + PlatformLayer* GraphicsLayerCA::platformLayer() const { return primaryLayer(); @@ -1391,16 +1609,7 @@ void GraphicsLayerCA::setDebugBorder(const Color& color, float borderWidth) END_BLOCK_OBJC_EXCEPTIONS } - -void GraphicsLayerCA::setZPosition(float position) -{ - GraphicsLayer::setZPosition(position); - - BEGIN_BLOCK_OBJC_EXCEPTIONS - [primaryLayer() setZPosition:position]; - END_BLOCK_OBJC_EXCEPTIONS -} -#endif +#endif // NDEBUG bool GraphicsLayerCA::requiresTiledLayer(const FloatSize& size) const { @@ -1411,21 +1620,21 @@ bool GraphicsLayerCA::requiresTiledLayer(const FloatSize& size) const return size.width() > cMaxPixelDimension || size.height() > cMaxPixelDimension; } -void GraphicsLayerCA::swapFromOrToTiledLayer(bool userTiledLayer) +void GraphicsLayerCA::swapFromOrToTiledLayer(bool useTiledLayer) { - if (userTiledLayer == m_usingTiledLayer) + if (useTiledLayer == 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]; + Class layerClass = useTiledLayer ? [WebTiledLayer self] : [WebLayer self]; m_layer.adoptNS([[layerClass alloc] init]); - if (userTiledLayer) { + m_usingTiledLayer = useTiledLayer; + + if (useTiledLayer) { WebTiledLayer* tiledLayer = (WebTiledLayer*)m_layer.get(); [tiledLayer setTileSize:tileSize]; [tiledLayer setLevelsOfDetail:1]; @@ -1435,24 +1644,35 @@ void GraphicsLayerCA::swapFromOrToTiledLayer(bool userTiledLayer) [tiledLayer setContentsGravity:@"bottomLeft"]; else [tiledLayer setContentsGravity:@"topLeft"]; + +#if !HAVE_MODERN_QUARTZCORE + // Tiled layer has issues with flipped coordinates. + setContentsOrientation(CompositingCoordinatesTopDown); +#endif + } else { +#if !HAVE_MODERN_QUARTZCORE + setContentsOrientation(defaultContentsOrientation()); +#endif } [m_layer.get() setLayerOwner:this]; [m_layer.get() setSublayers:[oldLayer.get() sublayers]]; [[oldLayer.get() superlayer] replaceSublayer:oldLayer.get() with:m_layer.get()]; + + updateContentsTransform(); + + updateLayerPosition(); + updateLayerSize(); + updateAnchorPoint(); + updateTransform(); + updateChildrenTransform(); + updateMasksToBounds(); + updateContentsOpaque(); + updateBackfaceVisibility(); + updateLayerBackgroundColor(); - [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 + updateOpacityOnLayer(); #ifndef NDEBUG String name = String::format("CALayer(%p) GraphicsLayer(%p) ", m_layer.get(), this) + m_name; @@ -1467,68 +1687,84 @@ void GraphicsLayerCA::swapFromOrToTiledLayer(bool userTiledLayer) // need to tell new layer to draw itself setNeedsDisplay(); - END_BLOCK_OBJC_EXCEPTIONS - - m_usingTiledLayer = userTiledLayer; - #ifndef NDEBUG updateDebugIndicators(); #endif } -void GraphicsLayerCA::setContentsLayer(WebLayer* contentsLayer) +GraphicsLayer::CompositingCoordinatesOrientation GraphicsLayerCA::defaultContentsOrientation() const { - if (contentsLayer == m_contentsLayer) - return; - - BEGIN_BLOCK_OBJC_EXCEPTIONS +#if !HAVE_MODERN_QUARTZCORE + // Older QuartzCore does not support -geometryFlipped, so we manually flip the root + // layer geometry, and then flip the contents of each layer back so that the CTM for CG + // is unflipped, allowing it to do the correct font auto-hinting. + return CompositingCoordinatesBottomUp; +#else + return CompositingCoordinatesTopDown; +#endif +} - if (m_contentsLayer) { - [m_contentsLayer.get() removeFromSuperlayer]; - m_contentsLayer = 0; +void GraphicsLayerCA::updateContentsTransform() +{ +#if !HAVE_MODERN_QUARTZCORE + if (contentsOrientation() == CompositingCoordinatesBottomUp) { + CGAffineTransform contentsTransform = CGAffineTransformMakeScale(1, -1); + contentsTransform = CGAffineTransformTranslate(contentsTransform, 0, -[m_layer.get() bounds].size.height); + [m_layer.get() setContentsTransform:contentsTransform]; } - - if (contentsLayer) { - // Turn off implicit animations on the inner layer. - [contentsLayer setStyle:[NSDictionary dictionaryWithObject:nullActionsDictionary() forKey:@"actions"]]; - - m_contentsLayer.adoptNS([contentsLayer retain]); - - bool needToFlip = GraphicsLayer::compositingCoordinatesOrientation() == GraphicsLayer::CompositingCoordinatesBottomUp; - CGPoint anchorPoint = needToFlip ? CGPointMake(0.0f, 1.0f) : CGPointZero; - - // If the layer world is flipped, we need to un-flip the contents layer - if (needToFlip) { - CATransform3D flipper = { - 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, -1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f}; - [m_contentsLayer.get() setTransform:flipper]; - } - [m_contentsLayer.get() setAnchorPoint:anchorPoint]; +#else + ASSERT(contentsOrientation() == CompositingCoordinatesTopDown); +#endif +} - // Insert the content layer first. Video elements require this, because they have - // shadow content that must display in front of the video. - [m_layer.get() insertSublayer:m_contentsLayer.get() atIndex:0]; +void GraphicsLayerCA::setupContentsLayer(CALayer* contentsLayer) +{ + // Turn off implicit animations on the inner layer. + [contentsLayer setStyle:[NSDictionary dictionaryWithObject:nullActionsDictionary() forKey:@"actions"]]; - updateContentsRect(); + if (defaultContentsOrientation() == CompositingCoordinatesBottomUp) { + 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}; + [contentsLayer setTransform:flipper]; + [contentsLayer setAnchorPoint:CGPointMake(0.0f, 1.0f)]; + } else + [contentsLayer setAnchorPoint:CGPointZero]; #ifndef NDEBUG - if (showDebugBorders()) { - setLayerBorderColor(m_contentsLayer.get(), Color(0, 0, 128, 180)); - [m_contentsLayer.get() setBorderWidth:1.0f]; - } -#endif + if (showDebugBorders()) { + setLayerBorderColor(contentsLayer, Color(0, 0, 128, 180)); + [contentsLayer setBorderWidth:1.0f]; } -#ifndef NDEBUG - updateDebugIndicators(); #endif +} - END_BLOCK_OBJC_EXCEPTIONS +void GraphicsLayerCA::setOpacityInternal(float accumulatedOpacity) +{ + [(preserves3D() ? m_layer.get() : primaryLayer()) setOpacity:accumulatedOpacity]; } -} // namespace WebCore +void GraphicsLayerCA::updateOpacityOnLayer() +{ +#if !HAVE_MODERN_QUARTZCORE + // Distribute opacity either to our own layer or to our children. We pass in the + // contribution from our parent(s). + distributeOpacity(parent() ? parent()->accumulatedOpacity() : 1); +#else + [primaryLayer() setOpacity:m_opacity]; +#endif +} +void GraphicsLayerCA::noteLayerPropertyChanged(LayerChangeFlags flags) +{ + if (!m_uncommittedChanges && m_client) + m_client->notifySyncRequired(this); + + m_uncommittedChanges |= flags; +} + +} // namespace WebCore #endif // USE(ACCELERATED_COMPOSITING) diff --git a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h index f90f258..54eea00 100644 --- a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h +++ b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h @@ -37,12 +37,14 @@ #import <QTKit/QTTime.h> @class QTMovie; @class QTMovieView; +@class QTMovieLayer; @class QTVideoRendererWebKitOnly; @class WebCoreMovieObserver; #else class QTMovie; class QTMovieView; class QTTime; +class QTMovieLayer; class QTVideoRendererWebKitOnly; class WebCoreMovieObserver; #endif @@ -93,6 +95,7 @@ private: void setRate(float); void setVolume(float); + void setPreservesPitch(bool); void setEndTime(float time); @@ -111,14 +114,37 @@ private: void setSize(const IntSize&); void paint(GraphicsContext*, const IntRect&); + void paintCurrentFrameInContext(GraphicsContext*, const IntRect&); + +#if USE(ACCELERATED_COMPOSITING) + bool supportsAcceleratedRendering() const; + void acceleratedRenderingStateChanged(); +#endif + + bool hasSingleSecurityOrigin() const; + MediaPlayer::MovieLoadType movieLoadType() const; void createQTMovie(const String& url); + void createQTMovie(NSURL *, NSDictionary *movieAttributes); + + enum MediaRenderingMode { MediaRenderingNone, MediaRenderingMovieView, MediaRenderingSoftwareRenderer, MediaRenderingMovieLayer }; + MediaRenderingMode currentRenderingMode() const; + MediaRenderingMode preferredRenderingMode() const; + void setUpVideoRendering(); void tearDownVideoRendering(); + bool hasSetUpVideoRendering() const; + void createQTMovieView(); void detachQTMovieView(); - void createQTVideoRenderer(); + + enum QTVideoRendererMode { QTVideoRendererModeDefault, QTVideoRendererModeListensForNewImages }; + void createQTVideoRenderer(QTVideoRendererMode rendererMode); void destroyQTVideoRenderer(); + + void createQTMovieLayer(); + void destroyQTMovieLayer(); + QTTime createQTTime(float time) const; void updateStates(); @@ -132,6 +158,8 @@ private: void cacheMovieScale(); bool metaDataAvailable() const { return m_qtMovie && m_readyState >= MediaPlayer::HaveMetadata; } + bool isReadyForRendering() const; + MediaPlayer* m_player; RetainPtr<QTMovie> m_qtMovie; RetainPtr<QTMovieView> m_qtMovieView; @@ -149,7 +177,10 @@ private: unsigned m_enabledTrackCount; unsigned m_totalTrackCount; bool m_hasUnsupportedTracks; - float m_duration; + float m_reportedDuration; + float m_cachedDuration; + float m_timeToRestore; + RetainPtr<QTMovieLayer> m_qtVideoLayer; #if DRAW_FRAME_RATE int m_frameCountWhilePlaying; double m_timeStartedPlaying; diff --git a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm index dd41ed3..c1d7fcb 100644 --- a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm +++ b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm @@ -44,6 +44,10 @@ #import <objc/objc-runtime.h> #import <wtf/UnusedParam.h> +#if USE(ACCELERATED_COMPOSITING) +#include "GraphicsLayer.h" +#endif + #if DRAW_FRAME_RATE #import "Font.h" #import "Frame.h" @@ -67,6 +71,7 @@ SOFT_LINK(QTKit, QTMakeTime, QTTime, (long long timeValue, long timeScale), (tim SOFT_LINK_CLASS(QTKit, QTMovie) SOFT_LINK_CLASS(QTKit, QTMovieView) +SOFT_LINK_CLASS(QTKit, QTMovieLayer) SOFT_LINK_POINTER(QTKit, QTTrackMediaTypeAttribute, NSString *) SOFT_LINK_POINTER(QTKit, QTMediaTypeAttribute, NSString *) @@ -85,6 +90,7 @@ SOFT_LINK_POINTER(QTKit, QTMovieLoadStateDidChangeNotification, NSString *) SOFT_LINK_POINTER(QTKit, QTMovieNaturalSizeAttribute, NSString *) SOFT_LINK_POINTER(QTKit, QTMovieCurrentSizeAttribute, NSString *) SOFT_LINK_POINTER(QTKit, QTMoviePreventExternalURLLinksAttribute, NSString *) +SOFT_LINK_POINTER(QTKit, QTMovieRateChangesPreservePitchAttribute, NSString *) SOFT_LINK_POINTER(QTKit, QTMovieRateDidChangeNotification, NSString *) SOFT_LINK_POINTER(QTKit, QTMovieSizeDidChangeNotification, NSString *) SOFT_LINK_POINTER(QTKit, QTMovieTimeDidChangeNotification, NSString *) @@ -100,6 +106,7 @@ SOFT_LINK_POINTER(QTKit, QTMovieApertureModeAttribute, NSString *) #define QTMovie getQTMovieClass() #define QTMovieView getQTMovieViewClass() +#define QTMovieLayer getQTMovieLayerClass() #define QTTrackMediaTypeAttribute getQTTrackMediaTypeAttribute() #define QTMediaTypeAttribute getQTMediaTypeAttribute() @@ -118,6 +125,7 @@ SOFT_LINK_POINTER(QTKit, QTMovieApertureModeAttribute, NSString *) #define QTMovieNaturalSizeAttribute getQTMovieNaturalSizeAttribute() #define QTMovieCurrentSizeAttribute getQTMovieCurrentSizeAttribute() #define QTMoviePreventExternalURLLinksAttribute getQTMoviePreventExternalURLLinksAttribute() +#define QTMovieRateChangesPreservePitchAttribute getQTMovieRateChangesPreservePitchAttribute() #define QTMovieRateDidChangeNotification getQTMovieRateDidChangeNotification() #define QTMovieSizeDidChangeNotification getQTMovieSizeDidChangeNotification() #define QTMovieTimeDidChangeNotification getQTMovieTimeDidChangeNotification() @@ -201,7 +209,9 @@ MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) , m_enabledTrackCount(0) , m_totalTrackCount(0) , m_hasUnsupportedTracks(false) - , m_duration(-1.0f) + , m_reportedDuration(-1.0f) + , m_cachedDuration(-1.0f) + , m_timeToRestore(-1.0f) #if DRAW_FRAME_RATE , m_frameCountWhilePlaying(0) , m_timeStartedPlaying(0) @@ -220,41 +230,49 @@ MediaPlayerPrivate::~MediaPlayerPrivate() void MediaPlayerPrivate::createQTMovie(const String& url) { + NSURL *cocoaURL = KURL(url); + NSDictionary *movieAttributes = [NSDictionary dictionaryWithObjectsAndKeys: + cocoaURL, QTMovieURLAttribute, + [NSNumber numberWithBool:m_player->preservesPitch()], QTMovieRateChangesPreservePitchAttribute, + [NSNumber numberWithBool:YES], QTMoviePreventExternalURLLinksAttribute, + [NSNumber numberWithBool:YES], QTSecurityPolicyNoCrossSiteAttribute, + [NSNumber numberWithBool:NO], QTMovieAskUnresolvedDataRefsAttribute, +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + [NSNumber numberWithBool:YES], @"QTMovieOpenForPlaybackAttribute", +#endif +#ifndef BUILDING_ON_TIGER + QTMovieApertureModeClean, QTMovieApertureModeAttribute, +#endif + nil]; + + createQTMovie(cocoaURL, movieAttributes); +} + +void MediaPlayerPrivate::createQTMovie(NSURL *url, NSDictionary *movieAttributes) +{ [[NSNotificationCenter defaultCenter] removeObserver:m_objcObserver.get()]; + bool recreating = false; if (m_qtMovie) { + recreating = true; destroyQTVideoRenderer(); m_qtMovie = 0; } - // Disable streaming support for now, <rdar://problem/5693967> - if (protocolIs(url, "rtsp")) + // Disable rtsp streams for now, <rdar://problem/5693967> + if (protocolIs([url scheme], "rtsp")) return; - - NSURL *cocoaURL = KURL(url); - NSDictionary *movieAttributes = [NSDictionary dictionaryWithObjectsAndKeys: - cocoaURL, QTMovieURLAttribute, - [NSNumber numberWithBool:YES], QTMoviePreventExternalURLLinksAttribute, - [NSNumber numberWithBool:YES], QTSecurityPolicyNoCrossSiteAttribute, - [NSNumber numberWithBool:NO], QTMovieAskUnresolvedDataRefsAttribute, -#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) - [NSNumber numberWithBool:YES], @"QTMovieOpenForPlaybackAttribute", -#endif -#ifndef BUILDING_ON_TIGER - QTMovieApertureModeClean, QTMovieApertureModeAttribute, -#endif - nil]; NSError *error = nil; m_qtMovie.adoptNS([[QTMovie alloc] initWithAttributes:movieAttributes error:&error]); - // FIXME: Find a proper way to detect streaming content. - m_isStreaming = protocolIs(url, "rtsp"); - if (!m_qtMovie) return; [m_qtMovie.get() setVolume:m_player->volume()]; + + if (recreating && hasVideo()) + createQTVideoRenderer(QTVideoRendererModeListensForNewImages); [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get() selector:@selector(loadStateChanged:) @@ -370,7 +388,7 @@ void MediaPlayerPrivate::detachQTMovieView() } } -void MediaPlayerPrivate::createQTVideoRenderer() +void MediaPlayerPrivate::createQTVideoRenderer(QTVideoRendererMode rendererMode) { destroyQTVideoRenderer(); @@ -381,11 +399,13 @@ void MediaPlayerPrivate::createQTVideoRenderer() // associate our movie with our instance of QTVideoRendererWebKitOnly [(id<WebKitVideoRenderingDetails>)m_qtVideoRenderer.get() setMovie:m_qtMovie.get()]; - // listen to QTVideoRendererWebKitOnly's QTVideoRendererWebKitOnlyNewImageDidBecomeAvailableNotification - [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get() - selector:@selector(newImageAvailable:) - name:QTVideoRendererWebKitOnlyNewImageAvailableNotification - object:m_qtVideoRenderer.get()]; + if (rendererMode == QTVideoRendererModeListensForNewImages) { + // listen to QTVideoRendererWebKitOnly's QTVideoRendererWebKitOnlyNewImageDidBecomeAvailableNotification + [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get() + selector:@selector(newImageAvailable:) + name:QTVideoRendererWebKitOnlyNewImageAvailableNotification + object:m_qtVideoRenderer.get()]; + } } void MediaPlayerPrivate::destroyQTVideoRenderer() @@ -404,23 +424,114 @@ void MediaPlayerPrivate::destroyQTVideoRenderer() m_qtVideoRenderer = nil; } -void MediaPlayerPrivate::setUpVideoRendering() +void MediaPlayerPrivate::createQTMovieLayer() +{ +#if USE(ACCELERATED_COMPOSITING) + if (!m_qtMovie) + return; + + ASSERT(supportsAcceleratedRendering()); + + if (!m_qtVideoLayer) { + m_qtVideoLayer.adoptNS([[QTMovieLayer alloc] init]); + if (!m_qtVideoLayer) + return; + + [m_qtVideoLayer.get() setMovie:m_qtMovie.get()]; +#ifndef NDEBUG + [(CALayer *)m_qtVideoLayer.get() setName:@"Video layer"]; +#endif + + // Hang the video layer from the render layer, if we have one yet. If not, we'll do this + // later via acceleratedRenderingStateChanged(). + GraphicsLayer* videoGraphicsLayer = m_player->mediaPlayerClient()->mediaPlayerGraphicsLayer(m_player); + if (videoGraphicsLayer) + videoGraphicsLayer->setContentsToVideo((PlatformLayer *)m_qtVideoLayer.get()); + } +#endif +} + +void MediaPlayerPrivate::destroyQTMovieLayer() +{ +#if USE(ACCELERATED_COMPOSITING) + if (!m_qtVideoLayer) + return; + + // disassociate our movie from our instance of QTMovieLayer + [m_qtVideoLayer.get() setMovie:nil]; + m_qtVideoLayer = nil; +#endif +} + +MediaPlayerPrivate::MediaRenderingMode MediaPlayerPrivate::currentRenderingMode() const +{ + if (m_qtMovieView) + return MediaRenderingMovieView; + + if (m_qtVideoLayer) + return MediaRenderingMovieLayer; + + if (m_qtVideoRenderer) + return MediaRenderingSoftwareRenderer; + + return MediaRenderingNone; +} + +MediaPlayerPrivate::MediaRenderingMode MediaPlayerPrivate::preferredRenderingMode() const { if (!m_player->frameView() || !m_qtMovie) + return MediaRenderingNone; + + if (m_player->inMediaDocument() || !QTVideoRendererClass()) + return MediaRenderingMovieView; + +#if USE(ACCELERATED_COMPOSITING) + if (supportsAcceleratedRendering() && m_player->mediaPlayerClient()->mediaPlayerRenderingCanBeAccelerated(m_player)) + return MediaRenderingMovieLayer; +#endif + + return MediaRenderingSoftwareRenderer; +} + +void MediaPlayerPrivate::setUpVideoRendering() +{ + MediaRenderingMode currentMode = currentRenderingMode(); + MediaRenderingMode preferredMode = preferredRenderingMode(); + if (currentMode == preferredMode && currentMode != MediaRenderingNone) return; - if (m_player->inMediaDocument() || !QTVideoRendererClass() ) + if (currentMode != MediaRenderingNone) + tearDownVideoRendering(); + + switch (preferredMode) { + case MediaRenderingMovieView: createQTMovieView(); - else - createQTVideoRenderer(); + break; + case MediaRenderingNone: + case MediaRenderingSoftwareRenderer: + createQTVideoRenderer(QTVideoRendererModeListensForNewImages); + break; + case MediaRenderingMovieLayer: + createQTMovieLayer(); + break; + } } void MediaPlayerPrivate::tearDownVideoRendering() { if (m_qtMovieView) detachQTMovieView(); - else + if (m_qtVideoRenderer) destroyQTVideoRenderer(); + if (m_qtVideoLayer) + destroyQTMovieLayer(); +} + +bool MediaPlayerPrivate::hasSetUpVideoRendering() const +{ + return m_qtMovieView + || m_qtVideoLayer + || m_qtVideoRenderer; } QTTime MediaPlayerPrivate::createQTTime(float time) const @@ -481,6 +592,10 @@ float MediaPlayerPrivate::duration() const { if (!metaDataAvailable()) return 0; + + if (m_cachedDuration != -1.0f) + return m_cachedDuration; + QTTime time = [m_qtMovie.get() duration]; if (time.flags == kQTTimeIsIndefinite) return numeric_limits<float>::infinity(); @@ -497,6 +612,10 @@ float MediaPlayerPrivate::currentTime() const void MediaPlayerPrivate::seek(float time) { + // Nothing to do if we are already in the middle of a seek to the same time. + if (time == m_seekTo) + return; + cancelSeek(); if (!metaDataAvailable()) @@ -506,7 +625,7 @@ void MediaPlayerPrivate::seek(float time) time = duration(); m_seekTo = time; - if (maxTimeLoaded() >= m_seekTo) + if (maxTimeSeekable() >= m_seekTo) doSeek(); else m_seekTimer.start(0, 0.5f); @@ -518,12 +637,16 @@ void MediaPlayerPrivate::doSeek() // setCurrentTime generates several event callbacks, update afterwards [m_objcObserver.get() setDelayCallbacks:YES]; float oldRate = [m_qtMovie.get() rate]; - [m_qtMovie.get() setRate:0]; + + if (oldRate) + [m_qtMovie.get() setRate:0]; [m_qtMovie.get() setCurrentTime:qttime]; - float timeAfterSeek = currentTime(); + // restore playback only if not at end, othewise QTMovie will loop + float timeAfterSeek = currentTime(); if (oldRate && timeAfterSeek < duration()) [m_qtMovie.get() setRate:oldRate]; + cancelSeek(); [m_objcObserver.get() setDelayCallbacks:NO]; } @@ -542,8 +665,8 @@ void MediaPlayerPrivate::seekTimerFired(Timer<MediaPlayerPrivate>*) m_player->timeChanged(); return; } - - if (maxTimeLoaded() >= m_seekTo) + + if (maxTimeSeekable() >= m_seekTo) doSeek(); else { MediaPlayer::NetworkState state = networkState(); @@ -607,8 +730,25 @@ void MediaPlayerPrivate::setRate(float rate) { if (!metaDataAvailable()) return; - if (!paused()) - [m_qtMovie.get() setRate:rate]; + [m_qtMovie.get() setRate:rate]; +} + +void MediaPlayerPrivate::setPreservesPitch(bool preservesPitch) +{ + if (!m_qtMovie) + return; + + // QTMovieRateChangesPreservePitchAttribute cannot be changed dynamically after QTMovie creation. + // If the passed in value is different than what already exists, we need to recreate the QTMovie for it to take effect. + if ([[m_qtMovie.get() attributeForKey:QTMovieRateChangesPreservePitchAttribute] boolValue] == preservesPitch) + return; + + NSDictionary *movieAttributes = [[m_qtMovie.get() movieAttributes] mutableCopy]; + ASSERT(movieAttributes); + [movieAttributes setValue:[NSNumber numberWithBool:preservesPitch] forKey:QTMovieRateChangesPreservePitchAttribute]; + m_timeToRestore = currentTime(); + + createQTMovie([movieAttributes valueForKey:QTMovieURLAttribute], movieAttributes); } int MediaPlayerPrivate::dataRate() const @@ -621,8 +761,7 @@ int MediaPlayerPrivate::dataRate() const float MediaPlayerPrivate::maxTimeBuffered() const { - // rtsp streams are not buffered - return m_isStreaming ? 0 : maxTimeLoaded(); + return maxTimeLoaded(); } float MediaPlayerPrivate::maxTimeSeekable() const @@ -701,6 +840,11 @@ void MediaPlayerPrivate::cacheMovieScale() m_scaleFactor.setHeight(initialSize.height / naturalSize.height); } +bool MediaPlayerPrivate::isReadyForRendering() const +{ + return m_readyState >= MediaPlayer::HaveMetadata && m_player->visible(); +} + void MediaPlayerPrivate::updateStates() { MediaPlayer::NetworkState oldNetworkState = m_networkState; @@ -711,19 +855,33 @@ void MediaPlayerPrivate::updateStates() if (loadState >= QTMovieLoadStateLoaded && m_readyState < MediaPlayer::HaveMetadata) { disableUnsupportedTracks(); if (m_player->inMediaDocument()) { - if (!m_enabledTrackCount || m_enabledTrackCount != m_totalTrackCount) { - // This is a type of media that we do not handle directly with a <video> - // element, likely streamed media or QuickTime VR. Tell the MediaPlayerClient + if (!m_enabledTrackCount || m_hasUnsupportedTracks) { + // This has a type of media that we do not handle directly with a <video> + // element, eg. a rtsp track or QuickTime VR. Tell the MediaPlayerClient // that we noticed. sawUnsupportedTracks(); return; } - } else if (!m_enabledTrackCount) { + } else if (!m_enabledTrackCount) loadState = QTMovieLoadStateError; - } - - if (loadState != QTMovieLoadStateError) + + if (loadState != QTMovieLoadStateError) { cacheMovieScale(); + MediaPlayer::MovieLoadType movieType = movieLoadType(); + m_isStreaming = movieType == MediaPlayer::StoredStream || movieType == MediaPlayer::LiveStream; + } + } + + // If this movie is reloading and we mean to restore the current time/rate, this might be the right time to do it. + if (loadState >= QTMovieLoadStateLoaded && oldNetworkState < MediaPlayer::Loaded && m_timeToRestore != -1.0f) { + QTTime qttime = createQTTime(m_timeToRestore); + m_timeToRestore = -1.0f; + + // Disable event callbacks from setCurrentTime for restoring time in a recreated video + [m_objcObserver.get() setDelayCallbacks:YES]; + [m_qtMovie.get() setCurrentTime:qttime]; + [m_qtMovie.get() setRate:m_player->rate()]; + [m_objcObserver.get() setDelayCallbacks:NO]; } BOOL completelyLoaded = !m_isStreaming && (loadState >= QTMovieLoadStateComplete); @@ -751,6 +909,8 @@ void MediaPlayerPrivate::updateStates() m_readyState = MediaPlayer::HaveNothing; m_networkState = MediaPlayer::Loading; } else { + // Loading or decoding failed. + if (m_player->inMediaDocument()) { // Something went wrong in the loading of media within a standalone file. // This can occur with chained refmovies pointing to streamed media. @@ -773,23 +933,28 @@ void MediaPlayerPrivate::updateStates() } } + if (isReadyForRendering() && !hasSetUpVideoRendering()) + setUpVideoRendering(); + if (seeking()) - m_readyState = MediaPlayer::HaveNothing; + m_readyState = m_readyState >= MediaPlayer::HaveMetadata ? MediaPlayer::HaveMetadata : MediaPlayer::HaveNothing; + + // Streaming movies don't use the network when paused. + if (m_isStreaming && m_readyState >= MediaPlayer::HaveMetadata && m_networkState >= MediaPlayer::Loading && [m_qtMovie.get() rate] == 0) + m_networkState = MediaPlayer::Idle; if (m_networkState != oldNetworkState) m_player->networkStateChanged(); + if (m_readyState != oldReadyState) m_player->readyStateChanged(); - if (loadState >= QTMovieLoadStateLoaded && (!m_qtMovieView && !m_qtVideoRenderer) && m_player->visible()) - setUpVideoRendering(); - if (loadState >= QTMovieLoadStateLoaded) { float dur = duration(); - if (dur != m_duration) { - if (m_duration != -1.0f) + if (dur != m_reportedDuration) { + if (m_reportedDuration != -1.0f) m_player->durationChanged(); - m_duration = dur; + m_reportedDuration = dur; } } } @@ -820,6 +985,13 @@ void MediaPlayerPrivate::timeChanged() if (m_hasUnsupportedTracks) return; + // It may not be possible to seek to a specific time in a streamed movie. When seeking in a + // stream QuickTime sets the movie time to closest time possible and posts a timechanged + // notification. Update m_seekTo so we can detect when the seek completes. + if (m_seekTo != -1) + m_seekTo = currentTime(); + + m_timeToRestore = -1.0f; updateStates(); m_player->timeChanged(); } @@ -833,6 +1005,12 @@ void MediaPlayerPrivate::didEnd() #if DRAW_FRAME_RATE m_timeStoppedPlaying = [NSDate timeIntervalSinceReferenceDate]; #endif + + // Hang onto the current time and use it as duration from now on since QuickTime is telling us we + // are at the end. Do this because QuickTime sometimes reports one time for duration and stops + // playback at another time, which causes problems in HTMLMediaElement. + m_cachedDuration = currentTime(); + updateStates(); m_player->timeChanged(); } @@ -877,6 +1055,20 @@ void MediaPlayerPrivate::repaint() m_player->repaint(); } +void MediaPlayerPrivate::paintCurrentFrameInContext(GraphicsContext* context, const IntRect& r) +{ + id qtVideoRenderer = m_qtVideoRenderer.get(); + if (!qtVideoRenderer && currentRenderingMode() == MediaRenderingMovieLayer) { + // We're being told to render into a context, but we already have the + // MovieLayer going. This probably means we've been called from <canvas>. + // Set up a QTVideoRenderer to use, but one that doesn't register for + // update callbacks. That way, it won't bother us asking to repaint. + createQTVideoRenderer(QTVideoRendererModeDefault); + qtVideoRenderer = m_qtVideoRenderer.get(); + } + paint(context, r); +} + void MediaPlayerPrivate::paint(GraphicsContext* context, const IntRect& r) { if (context->paintingDisabled() || m_hasUnsupportedTracks) @@ -944,12 +1136,11 @@ void MediaPlayerPrivate::paint(GraphicsContext* context, const IntRect& r) TextRun textRun(text.characters(), text.length()); const Color color(255, 0, 0); context->scale(FloatSize(1.0f, -1.0f)); - context->setFont(styleToUse->font()); context->setStrokeColor(color); context->setStrokeStyle(SolidStroke); context->setStrokeThickness(1.0f); context->setFillColor(color); - context->drawText(textRun, IntPoint(2, -3)); + context->drawText(styleToUse->font(), textRun, IntPoint(2, -3)); } } #endif @@ -1029,7 +1220,7 @@ MediaPlayer::SupportsType MediaPlayerPrivate::supportsType(const String& type, c // We check the "modern" type cache first, as it doesn't require QTKitServer to start. if (mimeModernTypesCache().contains(type) || mimeCommonTypesCache().contains(type)) - return (codecs && !codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported); + return codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported; return MediaPlayer::IsNotSupported; } @@ -1092,8 +1283,10 @@ void MediaPlayerPrivate::disableUnsupportedTracks() // Check to see if the track is disabled already, we should move along. // We don't need to re-disable it. - if (![track isEnabled]) + if (![track isEnabled]) { + --m_enabledTrackCount; continue; + } // Get the track's media type. NSString *mediaType = [track attributeForKey:QTTrackMediaTypeAttribute]; @@ -1105,6 +1298,7 @@ void MediaPlayerPrivate::disableUnsupportedTracks() // If this track type is not allowed, then we need to disable it. [track setEnabled:NO]; --m_enabledTrackCount; + m_hasUnsupportedTracks = true; } // Disable chapter tracks. These are most likely to lead to trouble, as @@ -1137,6 +1331,7 @@ void MediaPlayerPrivate::disableUnsupportedTracks() // Disable the evil, evil track. [chapterTrack setEnabled:NO]; --m_enabledTrackCount; + m_hasUnsupportedTracks = true; } } @@ -1146,8 +1341,50 @@ void MediaPlayerPrivate::sawUnsupportedTracks() m_player->mediaPlayerClient()->mediaPlayerSawUnsupportedTracks(m_player); } +#if USE(ACCELERATED_COMPOSITING) +bool MediaPlayerPrivate::supportsAcceleratedRendering() const +{ + // When in the media document we render via QTMovieView, which is already accelerated. + return isReadyForRendering() && getQTMovieLayerClass() != Nil && !m_player->inMediaDocument(); } +void MediaPlayerPrivate::acceleratedRenderingStateChanged() +{ + // Set up or change the rendering path if necessary. + setUpVideoRendering(); + + if (currentRenderingMode() == MediaRenderingMovieLayer) { + GraphicsLayer* videoGraphicsLayer = m_player->mediaPlayerClient()->mediaPlayerGraphicsLayer(m_player); + if (videoGraphicsLayer) + videoGraphicsLayer->setContentsToVideo((PlatformLayer *)m_qtVideoLayer.get()); + } +} +#endif + +bool MediaPlayerPrivate::hasSingleSecurityOrigin() const +{ + // We tell quicktime to disallow resources that come from different origins + // so we know all media is single origin. + return true; +} + +MediaPlayer::MovieLoadType MediaPlayerPrivate::movieLoadType() const +{ + if (!m_qtMovie) + return MediaPlayer::Unknown; + + MediaPlayer::MovieLoadType movieType = (MediaPlayer::MovieLoadType)wkQTMovieGetType(m_qtMovie.get()); + + // Can't include WebKitSystemInterface from WebCore so we can't get the enum returned + // by wkQTMovieGetType, but at least verify that the value is in the valid range. + ASSERT(movieType >= MediaPlayer::Unknown && movieType <= MediaPlayer::LiveStream); + + return movieType; +} + + +} // namespace WebCore + @implementation WebCoreMovieObserver - (id)initWithCallback:(MediaPlayerPrivate*)callback diff --git a/WebCore/platform/graphics/mac/WebLayer.h b/WebCore/platform/graphics/mac/WebLayer.h index b8b46ed..af53ae6 100644 --- a/WebCore/platform/graphics/mac/WebLayer.h +++ b/WebCore/platform/graphics/mac/WebLayer.h @@ -42,6 +42,13 @@ namespace WebCore { @end +#if defined(BUILDING_ON_LEOPARD) +@interface CALayer(WebLayerInternal) +- (CGAffineTransform)contentsTransform; +- (void)setContentsTransform:(CGAffineTransform)t; +@end +#endif + @interface WebLayer : CALayer { WebCore::GraphicsLayer* m_layerOwner; diff --git a/WebCore/platform/graphics/mac/WebLayer.mm b/WebCore/platform/graphics/mac/WebLayer.mm index fad3916..2647466 100644 --- a/WebCore/platform/graphics/mac/WebLayer.mm +++ b/WebCore/platform/graphics/mac/WebLayer.mm @@ -40,10 +40,18 @@ using namespace WebCore; + (void)drawContents:(WebCore::GraphicsLayer*)layerContents ofLayer:(CALayer*)layer intoContext:(CGContextRef)context { - UNUSED_PARAM(layer); + if (!layerContents) + return; + CGContextSaveGState(context); - - if (layerContents && layerContents->client()) { + + CGRect layerBounds = [layer bounds]; + if (layerContents->contentsOrientation() == WebCore::GraphicsLayer::CompositingCoordinatesBottomUp) { + CGContextScaleCTM(context, 1, -1); + CGContextTranslateCTM(context, 0, -layerBounds.size.height); + } + + if (layerContents->client()) { [NSGraphicsContext saveGraphicsState]; // Set up an NSGraphicsContext for the context, so that parts of AppKit that rely on @@ -68,29 +76,24 @@ using namespace WebCore; // FIXME: ideally we'd avoid calling -setNeedsDisplay on a layer that is a plain color, // so CA never makes backing store for it (which is what -setNeedsDisplay will do above). CGContextSetRGBFillColor(context, 0.0f, 1.0f, 0.0f, 1.0f); - CGRect aBounds = [layer bounds]; - CGContextFillRect(context, aBounds); + CGContextFillRect(context, layerBounds); } #endif - CGContextRestoreGState(context); - #ifndef NDEBUG - if (layerContents && layerContents->showRepaintCounter()) { + if (layerContents->showRepaintCounter()) { bool isTiledLayer = [layer isKindOfClass:[CATiledLayer class]]; char text[16]; // that's a lot of repaints snprintf(text, sizeof(text), "%d", layerContents->incrementRepaintCount()); - CGAffineTransform a = CGContextGetCTM(context); - CGContextSaveGState(context); if (isTiledLayer) CGContextSetRGBFillColor(context, 0.0f, 1.0f, 0.0f, 0.8f); else CGContextSetRGBFillColor(context, 1.0f, 0.0f, 0.0f, 0.8f); - CGRect aBounds = [layer bounds]; + CGRect aBounds = layerBounds; aBounds.size.width = 10 + 12 * strlen(text); aBounds.size.height = 25; @@ -105,6 +108,8 @@ using namespace WebCore; CGContextRestoreGState(context); } #endif + + CGContextRestoreGState(context); } // Disable default animations @@ -132,12 +137,19 @@ using namespace WebCore; - (void)setNeedsDisplayInRect:(CGRect)dirtyRect { if (m_layerOwner && m_layerOwner->client() && m_layerOwner->drawsContent()) { +#if defined(BUILDING_ON_LEOPARD) + dirtyRect = CGRectApplyAffineTransform(dirtyRect, [self contentsTransform]); +#endif [super setNeedsDisplayInRect:dirtyRect]; #ifndef NDEBUG if (m_layerOwner->showRepaintCounter()) { CGRect bounds = [self bounds]; - [super setNeedsDisplayInRect:CGRectMake(bounds.origin.x, bounds.origin.y, 46, 25)]; + CGRect indicatorRect = CGRectMake(bounds.origin.x, bounds.origin.y, 46, 25); +#if defined(BUILDING_ON_LEOPARD) + indicatorRect = CGRectApplyAffineTransform(indicatorRect, [self contentsTransform]); +#endif + [super setNeedsDisplayInRect:indicatorRect]; } #endif } diff --git a/WebCore/platform/graphics/mac/WebTiledLayer.mm b/WebCore/platform/graphics/mac/WebTiledLayer.mm index 1dd00ba..a1f5693 100644 --- a/WebCore/platform/graphics/mac/WebTiledLayer.mm +++ b/WebCore/platform/graphics/mac/WebTiledLayer.mm @@ -74,12 +74,19 @@ using namespace WebCore; - (void)setNeedsDisplayInRect:(CGRect)dirtyRect { if (m_layerOwner && m_layerOwner->client() && m_layerOwner->drawsContent()) { +#if defined(BUILDING_ON_LEOPARD) + dirtyRect = CGRectApplyAffineTransform(dirtyRect, [self contentsTransform]); +#endif [super setNeedsDisplayInRect:dirtyRect]; #ifndef NDEBUG if (m_layerOwner->showRepaintCounter()) { CGRect bounds = [self bounds]; - [super setNeedsDisplayInRect:CGRectMake(bounds.origin.x, bounds.origin.y, 46, 25)]; + CGRect indicatorRect = CGRectMake(bounds.origin.x, bounds.origin.y, 46, 25); +#if defined(BUILDING_ON_LEOPARD) + indicatorRect = CGRectApplyAffineTransform(indicatorRect, [self contentsTransform]); +#endif + [super setNeedsDisplayInRect:indicatorRect]; } #endif } |