summaryrefslogtreecommitdiffstats
path: root/WebCore/platform/graphics/mac
diff options
context:
space:
mode:
authorBen Murdoch <benm@google.com>2009-08-11 17:01:47 +0100
committerBen Murdoch <benm@google.com>2009-08-11 18:21:02 +0100
commit0bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5 (patch)
tree2943df35f62d885c89d01063cc528dd73b480fea /WebCore/platform/graphics/mac
parent7e7a70bfa49a1122b2597a1e6367d89eb4035eca (diff)
downloadexternal_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.h3
-rw-r--r--WebCore/platform/graphics/mac/ColorMac.mm60
-rw-r--r--WebCore/platform/graphics/mac/FontPlatformData.h6
-rw-r--r--WebCore/platform/graphics/mac/FontPlatformDataMac.mm9
-rw-r--r--WebCore/platform/graphics/mac/GraphicsLayerCA.h177
-rw-r--r--WebCore/platform/graphics/mac/GraphicsLayerCA.mm1906
-rw-r--r--WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h35
-rw-r--r--WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm353
-rw-r--r--WebCore/platform/graphics/mac/WebLayer.h7
-rw-r--r--WebCore/platform/graphics/mac/WebLayer.mm36
-rw-r--r--WebCore/platform/graphics/mac/WebTiledLayer.mm9
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
}