/* * CALayer.j * AppKit * * Created by Francisco Tolmasky. * Copyright 2008, 280 North, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ import import import import #include "../CoreGraphics/CGGeometry.h" #include "../CoreGraphics/CGAffineTransform.j" #define DOM(aLayer) aLayer._DOMElement var CALayerGeometryBoundsMask = 1, CALayerGeometryPositionMask = 2, CALayerGeometryAnchorPointMask = 4, CALayerGeometryAffineTransformMask = 8, CALayerGeometryParentSublayerTransformMask = 16; var USE_BUFFER = NO; var CALayerFrameOriginUpdateMask = 1, CALayerFrameSizeUpdateMask = 2, CALayerZPositionUpdateMask = 4, CALayerDisplayUpdateMask = 8, CALayerCompositeUpdateMask = 16, CALayerDOMUpdateMask = CALayerZPositionUpdateMask | CALayerFrameOriginUpdateMask | CALayerFrameSizeUpdateMask; var CALayerRegisteredRunLoopUpdates = nil; @implementation CALayer : CPObject { // Modifying the Layer Geometry CGRect _frame; CGRect _bounds; CGPoint _position; unsigned _zPosition; CGPoint _anchorPoint; CGAffineTransform _affineTransform; CGAffineTransform _sublayerTransform; CGAffineTransform _sublayerTransformForSublayers; CGRect _backingStoreFrame; CGRect _standardBackingStoreFrame; BOOL _hasSublayerTransform; BOOL _hasCustomBackingStoreFrame; // Style Attributes float _opacity; BOOL _isHidden; CPColor _backgroundColor; // Managing Layer Hierarchy CALayer _superlayer; CPMutableArray _sublayers; // Updating Layer Display unsigned _runLoopUpdateMask; BOOL _needsDisplayOnBoundsChange; // Modifyin the Delegate id _delegate; BOOL _delegateRespondsToDisplayLayerSelector; BOOL _delegateRespondsToDrawLayerInContextSelector; // DOM Implementation DOMElement _DOMElement; DOMElement _DOMContentsElement; id _contents; CGContext _context; CPView _owningView; CGAffineTransform _transformToLayer; CGAffineTransform _transformFromLayer; } + (CALayer)layer { return [[[self class] alloc] init]; } - (id)init { self = [super init]; if (self) { _frame = CGRectMakeZero(); _backingStoreFrame = CGRectMakeZero(); _standardBackingStoreFrame = CGRectMakeZero(); _bounds = CGRectMakeZero(); _position = CGPointMakeZero(); _zPosition = 0.0; _anchorPoint = CGPointMake(0.5, 0.5); _affineTransform = CGAffineTransformMakeIdentity(); _sublayerTransform = CGAffineTransformMakeIdentity(); _transformToLayer = CGAffineTransformMakeIdentity(); // FIXME? does it matter? _transformFromLayer = CGAffineTransformMakeIdentity(); _opacity = 1.0; _isHidden = NO; _masksToBounds = NO; _sublayers = []; _DOMElement = document.createElement("div"); _DOMElement.style.overflow = "visible"; _DOMElement.style.position = "absolute"; _DOMElement.style.visibility = "visible"; _DOMElement.style.top = "0px"; _DOMElement.style.left = "0px"; _DOMElement.style.zIndex = 0; _DOMElement.style.width = "0px"; _DOMElement.style.height = "0px"; } return self; } // Modifying the Layer Geometry - (void)setBounds:(CGRect)aBounds { if (CGRectEqualToRect(_bounds, aBounds)) return; var oldOrigin = _bounds.origin; _bounds = _CGRectMakeCopy(aBounds); if (_hasSublayerTransform) _CALayerUpdateSublayerTransformForSublayers(self); // _hasSublayerTransform == true will handle this for us. /*else if (!CGPointEqualToPoint(_bounds.origin, oldOrigin)) { var index = _sublayers.length; // FIXME: This should climb the layer tree down. while (index--) _CALayerRecalculateGeometry(_sublayers[index], CALayerGeometryPositionMask); }*/ _CALayerRecalculateGeometry(self, CALayerGeometryBoundsMask); } - (CGRect)bounds { return _bounds; } - (void)setPosition:(CGPoint)aPosition { if (CGPointEqualToPoint(_position, aPosition)) return; _position = _CGPointMakeCopy(aPosition); _CALayerRecalculateGeometry(self, CALayerGeometryPositionMask); } - (CGPoint)position { return _position; } - (void)setZPosition:(int)aZPosition { if (_zPosition == aZPosition) return; _zPosition = aZPosition; [self registerRunLoopUpdateWithMask:CALayerZPositionUpdateMask]; } - (void)setAnchorPoint:(CGPoint)anAnchorPoint { anAnchorPoint = _CGPointMakeCopy(anAnchorPoint); anAnchorPoint.x = MIN(1.0, MAX(0.0, anAnchorPoint.x)); anAnchorPoint.y = MIN(1.0, MAX(0.0, anAnchorPoint.y)); if (CGPointEqualToPoint(_anchorPoint, anAnchorPoint)) return; _anchorPoint = anAnchorPoint; if (_hasSublayerTransform) _CALayerUpdateSublayerTransformForSublayers(self); if (_owningView) _position = CGPointMake(_CGRectGetWidth(_bounds) * _anchorPoint.x, _CGRectGetHeight(_bounds) * _anchorPoint.y); _CALayerRecalculateGeometry(self, CALayerGeometryAnchorPointMask); } - (CGPoint)anchorPoint { return _anchorPoint; } - (void)setAffineTransform:(CGAffineTransform)anAffineTransform { if (CGAffineTransformEqualToTransform(_affineTransform, anAffineTransform)) return; _affineTransform = _CGAffineTransformMakeCopy(anAffineTransform); _CALayerRecalculateGeometry(self, CALayerGeometryAffineTransformMask); } - (CGAffineTransform)affineTransform { return _affineTransform; } - (void)setSublayerTransform:(CGAffineTransform)anAffineTransform { if (CGAffineTransformEqualToTransform(_sublayerTransform, anAffineTransform)) return; var hadSublayerTransform = _hasSublayerTransform; _sublayerTransform = _CGAffineTransformMakeCopy(anAffineTransform); _hasSublayerTransform = !_CGAffineTransformIsIdentity(_sublayerTransform); if (_hasSublayerTransform) { _CALayerUpdateSublayerTransformForSublayers(self); var index = _sublayers.length; // FIXME: This should climb the layer tree down. while (index--) _CALayerRecalculateGeometry(_sublayers[index], CALayerGeometryParentSublayerTransformMask); } } - (CGAffineTransform)sublayerTransform { return _sublayerTransform; } - (CGAffineTransform)transformToLayer { return _transformToLayer; } - (void)setFrame:(CGRect)aFrame { alert("FIXME IMPLEMENT"); } // The frame defines the bounding box of the layer: the smallest // possible rectangle that could fit this layer after transform // properties are applied in superlayer coordinates. - (CGRect)frame { if (!_frame) _frame = [self convertRect:_bounds toLayer:_superlayer]; return _frame; } // The Backing Store Frame specifies the frame of the actual backing // store used to contain this layer. Naturally, by default it is the // same as the frame, however, users can specify their own custom // Backing Store Frame in order to speed up certain operations, such as // live transformation. - (CGRect)backingStoreFrame { return _backingStoreFrame; } - (void)setBackingStoreFrame:(CGRect)aFrame { _hasCustomBackingStoreFrame = (aFrame != nil); if (aFrame == nil) aFrame = CGRectMakeCopy(_standardBackingStoreFrame); else { if (_superlayer) { aFrame = [_superlayer convertRect:aFrame toLayer:nil]; var bounds = [_superlayer bounds], frame = [_superlayer convertRect:bounds toLayer:nil]; aFrame.origin.x -= _CGRectGetMinX(frame); aFrame.origin.y -= _CGRectGetMinY(frame); } else aFrame = CGRectMakeCopy(aFrame); } if (!CGPointEqualToPoint(_backingStoreFrame.origin, aFrame.origin)) [self registerRunLoopUpdateWithMask:CALayerFrameOriginUpdateMask]; if (!CGSizeEqualToSize(_backingStoreFrame.size, aFrame.size)) [self registerRunLoopUpdateWithMask:CALayerFrameSizeUpdateMask]; _backingStoreFrame = aFrame; } // Providing Layer Content - (CGImage)contents { return _contents; } - (void)setContents:(CGImage)contents { if (_contents == contents) return; _contents = contents; [self composite]; } - (void)composite { if (USE_BUFFER && !_contents || !_context) return; CGContextClearRect(_context, _CGRectMake(0.0, 0.0, _CGRectGetWidth(_backingStoreFrame), _CGRectGetHeight(_backingStoreFrame))); // Recomposite var transform = _transformFromLayer; if (_superlayer) { var superlayerTransform = _CALayerGetTransform(_superlayer, nil), superlayerOrigin = CGPointApplyAffineTransform(_superlayer._bounds.origin, superlayerTransform); transform = CGAffineTransformConcat(transform, superlayerTransform); transform.tx -= superlayerOrigin.x; transform.ty -= superlayerOrigin.y; } transform.tx -= _CGRectGetMinX(_backingStoreFrame); transform.ty -= _CGRectGetMinY(_backingStoreFrame); CGContextSaveGState(_context); CGContextConcatCTM(_context, transform);//_transformFromView); if (USE_BUFFER) { // CGContextDrawImage(_context, _bounds, _contents.context); _context.drawImage(_contents.buffer, _CGRectGetMinX(_bounds), _CGRectGetMinY(_bounds));//, _CGRectGetWidth(_standardBackingStoreFrame), _CGRectGetHeight(_standardBackingStoreFrame)); } else [self drawInContext:_context]; CGContextRestoreGState(_context); } - (void)display { if (!_context) { _context = CGBitmapGraphicsContextCreate(); _DOMContentsElement = _context.DOMElement; _DOMContentsElement.style.zIndex = -100; _DOMContentsElement.style.overflow = "hidden"; _DOMContentsElement.style.position = "absolute"; _DOMContentsElement.style.visibility = "visible"; _DOMContentsElement.width = ROUND(_CGRectGetWidth(_backingStoreFrame)); _DOMContentsElement.height = ROUND(_CGRectGetHeight(_backingStoreFrame)); _DOMContentsElement.style.top = "0px"; _DOMContentsElement.style.left = "0px"; _DOMContentsElement.style.width = ROUND(_CGRectGetWidth(_backingStoreFrame)) + "px"; _DOMContentsElement.style.height = ROUND(_CGRectGetHeight(_backingStoreFrame)) + "px"; _DOMElement.appendChild(_DOMContentsElement); } if (USE_BUFFER) { if (_delegateRespondsToDisplayLayerSelector) return [self displayInLayer:self]; if (_CGRectGetWidth(_backingStoreFrame) == 0.0 || _CGRectGetHeight(_backingStoreFrame) == 0.0) return; if (!_contents) _contents = CABackingStoreCreate(); CABackingStoreSetSize(_contents, _bounds.size); [self drawInContext:CABackingStoreGetContext(_contents)]; } [self composite]; } - (void)drawInContext:(CGContext)aContext { //if (!window.loop || window.nodisplay) CPLog.error("htiasd"); if (_backgroundColor) { CGContextSetFillColor(aContext, _backgroundColor); CGContextFillRect(aContext, _bounds); } if (_delegateRespondsToDrawLayerInContextSelector) [_delegate drawLayer:self inContext:aContext]; } // Style Attributes - (float)opacity { return _opacity; } - (void)setOpacity:(float)anOpacity { if (_opacity == anOpacity) return; _opacity = anOpacity; _DOMElement.style.opacity = anOpacity; _DOMElement.style.filter = "alpha(opacity=" + anOpacity * 100 + ")"; } - (void)setHidden:(BOOL)isHidden { _isHidden = isHidden; _DOMElement.style.display = isHidden ? "none" : "block"; } - (BOOL)hidden { return _isHidden; } - (BOOL)isHidden { return _isHidden; } - (void)setMasksToBounds:(BOOL)masksToBounds { if (_masksToBounds == masksToBounds) return; _masksToBounds = masksToBounds; _DOMElement.style.overflow = _masksToBounds ? "hidden" : "visible"; } - (void)setBackgroundColor:(CPColor)aColor { _backgroundColor = aColor; [self setNeedsDisplay]; } - (CPColor)backgroundColor { return _backgroundColor; } // Managing Layer Hierarchy - (CPArray)sublayers { return _sublayers; } - (CALayer)superlayer { return _superlayer; } #define ADJUST_CONTENTS_ZINDEX(aLayer)\ if (_DOMContentsElement && aLayer._zPosition > _DOMContentsElement.style.zIndex)\ _DOMContentsElement.style.zIndex -= 100.0;\ - (void)addSublayer:(CALayer)aLayer { [self insertSublayer:aLayer atIndex:_sublayers.length]; return; ADJUST_CONTENTS_ZINDEX(aLayer); [_sublayers addObject:aLayer]; _DOMElement.appendChild(DOM(aLayer)); } - (void)removeFromSuperlayer { if (_owningView) [_owningView setLayer:nil]; if (!_superlayer) return; _superlayer._DOMElement.removeChild(_DOMElement); [_superlayer._sublayers removeObject:self]; _superlayer = nil; } - (void)insertSublayer:(CALayer)aLayer atIndex:(unsigned)anIndex { if (!aLayer) return; var superlayer = [aLayer superlayer]; if (superlayer == self) { var index = [_sublayers indexOfObjectIdenticalTo:aLayer]; if (index == anIndex) return; [_sublayers removeObjectAtIndex:index]; if (index < anIndex) --anIndex; } else if (superlayer != nil) [aLayer removeFromSuperlayer]; ADJUST_CONTENTS_ZINDEX(aLayer); [_sublayers insertObject:aLayer atIndex:anIndex]; if (anIndex >= _sublayers.length - 1) _DOMElement.appendChild(DOM(aLayer)); else _DOMElement.insertBefore(DOM(aLayer), _sublayers[anIndex + 1]._DOMElement); aLayer._superlayer = self; if (self != superlayer) _CALayerRecalculateGeometry(aLayer, 0xFFFFFFF); } - (void)insertSublayer:(CALayer)aLayer below:(CALayer)aSublayer { var index = aSublayer ? [_sublayers indexOfObjectIdenticalTo:aSublayer] : 0; [self insertSublayer:aLayer atIndex:index == CPNotFound ? _sublayers.length : index]; } - (void)insertSublayer:(CALayer)aLayer after:(CALayer)aSublayer { var index = aSublayer ? [_sublayers indexOfObjectIdenticalTo:aSublayer] : _sublayers.length; [_sublayers insertObject:aLayer atIndex:index == CPNotFound ? _sublayers.length : index + 1]; } - (void)replaceSublayer:(CALayer)aSublayer with:(CALayer)aLayer { if (aSublayer == aLayer) return; // FIXME: EXCEPTION if (aSublayer._superlayer != self) { alert("EXCEPTION"); return; } ADJUST_CONTENTS_ZINDEX(aLayer); [_sublayers replaceObjectAtIndex:[_sublayers indexOfObjectIdenticalTo:aSublayer] withObject:aLayer]; _DOMElement.replaceChild(DOM(aSublayer), DOM(aLayer)); } // Updating Layer Display + (void)runLoopUpdateLayers {if (window.oops) {alert(window.latest); objj_debug_print_backtrace();} window.loop = true; for (hash in CALayerRegisteredRunLoopUpdates) { var layer = CALayerRegisteredRunLoopUpdates[hash], mask = layer._runLoopUpdateMask; if (mask & CALayerDOMUpdateMask) _CALayerUpdateDOM(layer, mask); if (mask & CALayerDisplayUpdateMask) [layer display]; else if (mask & CALayerFrameSizeUpdateMask || mask & CALayerCompositeUpdateMask) [layer composite]; layer._runLoopUpdateMask = 0; } window.loop= false; CALayerRegisteredRunLoopUpdates = nil; } - (void)registerRunLoopUpdateWithMask:(unsigned)anUpdateMask { if (CALayerRegisteredRunLoopUpdates == nil) { CALayerRegisteredRunLoopUpdates = {}; [[CPRunLoop currentRunLoop] performSelector:@selector(runLoopUpdateLayers) target:CALayer argument:nil order:0 modes:[CPDefaultRunLoopMode]]; } _runLoopUpdateMask |= anUpdateMask; CALayerRegisteredRunLoopUpdates[[self hash]] = self; } - (void)setNeedsComposite { [self registerRunLoopUpdateWithMask:CALayerCompositeUpdateMask]; } - (void)setNeedsDisplay { [self registerRunLoopUpdateWithMask:CALayerDisplayUpdateMask]; } - (void)setNeedsDisplayOnBoundsChange:(BOOL)needsDisplayOnBoundsChange { _needsDisplayOnBoundsChange = needsDisplayOnBoundsChange; } - (BOOL)needsDisplayOnBoundsChange { return _needsDisplayOnBoundsChange; } - (void)setNeedsDisplayInRect:(CGRect)aRect { _dirtyRect = aRect; [self display]; } // Mapping Between Coordinate and Time Spaces - (CGPoint)convertPoint:(CGPoint)aPoint fromLayer:(CALayer)aLayer { return CGPointApplyAffineTransform(aPoint, _CALayerGetTransform(aLayer, self)); } - (CGPoint)convertPoint:(CGPoint)aPoint toLayer:(CALayer)aLayer { return CGPointApplyAffineTransform(aPoint, _CALayerGetTransform(self, aLayer)); } - (CGRect)convertRect:(CGRect)aRect fromLayer:(CALayer)aLayer { return CGRectApplyAffineTransform(aRect, _CALayerGetTransform(aLayer, self)); } - (CGRect)convertRect:(CGRect)aRect toLayer:(CALayer)aLayer { return CGRectApplyAffineTransform(aRect, _CALayerGetTransform(self, aLayer)); } // Hit Testing - (BOOL)containsPoint:(CGPoint)aPoint { return _CGRectContainsPoint(_bounds, aPoint); } - (CALayer)hitTest:(CGPoint)aPoint { if (_isHidden) return nil; var point = CGPointApplyAffineTransform(aPoint, _transformToLayer); //alert(point.x + " " + point.y); if (!_CGRectContainsPoint(_bounds, point)) return nil; var layer = nil, index = _sublayers.length; // FIXME: this should take into account zPosition. while (index--) if (layer = [_sublayers[index] hitTest:point]) return layer; return self; } // Modifying the Delegate - (void)setDelegate:(id)aDelegate { if (_delegate == aDelegate) return; _delegate = aDelegate; _delegateRespondsToDisplayLayerSelector = [_delegate respondsToSelector:@selector(displayLayer:)]; _delegateRespondsToDrawLayerInContextSelector = [_delegate respondsToSelector:@selector(drawLayer:inContext:)]; if (_delegateRespondsToDisplayLayerSelector || _delegateRespondsToDrawLayerInContextSelector) [self setNeedsDisplay]; } - (id)delegate { return _delegate; } - (void)_setOwningView:(CPView)anOwningView { _owningView = anOwningView; if (_owningView) { _owningView = anOwningView; _bounds.size = CGSizeMakeCopy([_owningView bounds].size); _position = CGPointMake(_CGRectGetWidth(_bounds) * _anchorPoint.x, _CGRectGetHeight(_bounds) * _anchorPoint.y); } _CALayerRecalculateGeometry(self, CALayerGeometryPositionMask | CALayerGeometryBoundsMask); } - (void)_owningViewBoundsChanged { _bounds.size = CGSizeMakeCopy([_owningView bounds].size); _position = CGPointMake(_CGRectGetWidth(_bounds) * _anchorPoint.x, _CGRectGetHeight(_bounds) * _anchorPoint.y); _CALayerRecalculateGeometry(self, CALayerGeometryPositionMask | CALayerGeometryBoundsMask); } - (void)_update { window.loop = true; var mask = _runLoopUpdateMask; if (mask & CALayerDOMUpdateMask) _CALayerUpdateDOM(self, mask); if (mask & CALayerDisplayUpdateMask) [self display]; else if (mask & CALayerFrameSizeUpdateMask || mask & CALayerCompositeUpdateMask) [self composite]; _runLoopUpdateMask = 0; window.loop = false; } @end function _CALayerUpdateSublayerTransformForSublayers(aLayer) { var bounds = aLayer._bounds, anchorPoint = aLayer._anchorPoint, translateX = _CGRectGetWidth(bounds) * anchorPoint.x, translateY = _CGRectGetHeight(bounds) * anchorPoint.y; aLayer._sublayerTransformForSublayers = CGAffineTransformConcat( CGAffineTransformMakeTranslation(-translateX, -translateY), CGAffineTransformConcat(aLayer._sublayerTransform, CGAffineTransformMakeTranslation(translateX, translateY))); } function _CALayerUpdateDOM(aLayer, aMask) { var DOMElementStyle = aLayer._DOMElement.style; if (aMask & CALayerZPositionUpdateMask) DOMElementStyle.zIndex = aLayer._zPosition; var frame = aLayer._backingStoreFrame; if (aMask & CALayerFrameOriginUpdateMask) { DOMElementStyle.top = ROUND(_CGRectGetMinY(frame)) + "px"; DOMElementStyle.left = ROUND(_CGRectGetMinX(frame)) + "px"; } if (aMask & CALayerFrameSizeUpdateMask) { var width = MAX(0.0, ROUND(_CGRectGetWidth(frame))), height = MAX(0.0, ROUND(_CGRectGetHeight(frame))), DOMContentsElement = aLayer._DOMContentsElement; DOMElementStyle.width = width + "px"; DOMElementStyle.height = height + "px"; if (DOMContentsElement) { DOMContentsElement.width = width; DOMContentsElement.height = height; DOMContentsElement.style.width = width + "px"; DOMContentsElement.style.height = height + "px"; } } } function _CALayerRecalculateGeometry(aLayer, aGeometryChange) { var bounds = aLayer._bounds, superlayer = aLayer._superlayer, width = _CGRectGetWidth(bounds), height = _CGRectGetHeight(bounds), position = aLayer._position, anchorPoint = aLayer._anchorPoint, affineTransform = aLayer._affineTransform, backingStoreFrameSize = _CGSizeMakeCopy(aLayer._backingStoreFrame), hasCustomBackingStoreFrame = aLayer._hasCustomBackingStoreFrame; // Go to anchor, transform, go back to bounds. aLayer._transformFromLayer = CGAffineTransformConcat( CGAffineTransformMakeTranslation(-width * anchorPoint.x - _CGRectGetMinX(aLayer._bounds), -height * anchorPoint.y - _CGRectGetMinY(aLayer._bounds)), CGAffineTransformConcat(affineTransform, CGAffineTransformMakeTranslation(position.x, position.y))); if (superlayer && superlayer._hasSublayerTransform) { // aLayer._transformFromLayer = CGAffineTransformConcat(aLayer._transformFromLayer, superlayer._sublayerTransformForSublayers); _CGAffineTransformConcatTo(aLayer._transformFromLayer, superlayer._sublayerTransformForSublayers, aLayer._transformFromLayer); } aLayer._transformToLayer = CGAffineTransformInvert(aLayer._transformFromLayer); //aLayer._transformFromLayer.tx = ROUND(aLayer._transformFromLayer.tx); //aLayer._transformFromLayer.ty = ROUND(aLayer._transformFromLayer.ty); aLayer._frame = nil; aLayer._standardBackingStoreFrame = [aLayer convertRect:bounds toLayer:nil]; if (superlayer) { var bounds = [superlayer bounds], frame = [superlayer convertRect:bounds toLayer:nil]; aLayer._standardBackingStoreFrame.origin.x -= _CGRectGetMinX(frame); aLayer._standardBackingStoreFrame.origin.y -= _CGRectGetMinY(frame); } // We used to use CGRectIntegral here, but what we actually want, is the largest integral // rect that would ever contain this box, since for any width/height, there are 2 (4) // possible integral rects for it depending on it's position. It's OK that this is sometimes // bigger than the "optimal" bounding integral rect since that doesn't change drawing. var origin = aLayer._standardBackingStoreFrame.origin, size = aLayer._standardBackingStoreFrame.size; origin.x = FLOOR(origin.x); origin.y = FLOOR(origin.y); size.width = CEIL(size.width) + 1.0; size.height = CEIL(size.height) + 1.0; // FIXME: This avoids the central issue that a position change is sometimes a display and sometimes // a div move, and sometimes both. // Only use this frame if we don't currently have a custom backing store frame. if (!hasCustomBackingStoreFrame) { var backingStoreFrame = CGRectMakeCopy(aLayer._standardBackingStoreFrame); // These values get rounded in the DOM, so don't both updating them if they're // not going to be different after rounding. if (ROUND(_CGRectGetMinX(backingStoreFrame)) != ROUND(_CGRectGetMinX(aLayer._backingStoreFrame)) || ROUND(_CGRectGetMinY(backingStoreFrame)) != ROUND(_CGRectGetMinY(aLayer._backingStoreFrame))) [aLayer registerRunLoopUpdateWithMask:CALayerFrameOriginUpdateMask]; // Any change in size due to a geometry change is purely due to rounding error. if ((_CGRectGetWidth(backingStoreFrame) != ROUND(_CGRectGetWidth(aLayer._backingStoreFrame)) || _CGRectGetHeight(backingStoreFrame) != ROUND(_CGRectGetHeight(aLayer._backingStoreFrame)))) [aLayer registerRunLoopUpdateWithMask:CALayerFrameSizeUpdateMask]; aLayer._backingStoreFrame = backingStoreFrame; } if (aGeometryChange & CALayerGeometryBoundsMask && aLayer._needsDisplayOnBoundsChange) [aLayer setNeedsDisplay]; // We need to recomposite if we have a custom backing store frame, OR // If the change is not solely composed of position and anchor points changes. // Anchor point and position changes simply move the object, requiring // no re-rendering. else if (hasCustomBackingStoreFrame || (aGeometryChange & ~(CALayerGeometryPositionMask | CALayerGeometryAnchorPointMask))) [aLayer setNeedsComposite]; var sublayers = aLayer._sublayers, index = 0, count = sublayers.length; for (; index < count; ++index) _CALayerRecalculateGeometry(sublayers[index], aGeometryChange); } function _CALayerGetTransform(fromLayer, toLayer) { var transform = CGAffineTransformMakeIdentity(); if (fromLayer) { var layer = fromLayer; // If we have a fromLayer, "climb up" the layer tree until // we hit the root node or we hit the toLayer. while (layer && layer != toLayer) { var transformFromLayer = layer._transformFromLayer; //transform = CGAffineTransformConcat(transform, layer._transformFromLayer); _CGAffineTransformConcatTo(transform, transformFromLayer, transform); layer = layer._superlayer; } // If we hit toLayer, then we're done. if (layer == toLayer) return transform; } var layers = [], layer = toLayer; while (layer) { layers.push(layer); layer = layer._superlayer; } var index = layers.length; while (index--) { var transformToLayer = layers[index]._transformToLayer; _CGAffineTransformConcatTo(transform, transformToLayer, transform); } return transform; }