LOTLayerContainer.m 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. //
  2. // LOTLayerContainer.m
  3. // Lottie
  4. //
  5. // Created by brandon_withrow on 7/18/17.
  6. // Copyright © 2017 Airbnb. All rights reserved.
  7. //
  8. #import "LOTLayerContainer.h"
  9. #import "LOTTransformInterpolator.h"
  10. #import "LOTNumberInterpolator.h"
  11. #import "CGGeometry+LOTAdditions.h"
  12. #import "LOTRenderGroup.h"
  13. #import "LOTHelpers.h"
  14. #import "LOTMaskContainer.h"
  15. #import "LOTAsset.h"
  16. #if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
  17. #import "LOTCacheProvider.h"
  18. #endif
  19. @implementation LOTLayerContainer {
  20. LOTTransformInterpolator *_transformInterpolator;
  21. LOTNumberInterpolator *_opacityInterpolator;
  22. NSNumber *_inFrame;
  23. NSNumber *_outFrame;
  24. CALayer *DEBUG_Center;
  25. LOTRenderGroup *_contentsGroup;
  26. LOTMaskContainer *_maskLayer;
  27. }
  28. @dynamic currentFrame;
  29. - (instancetype)initWithModel:(LOTLayer *)layer
  30. inLayerGroup:(LOTLayerGroup *)layerGroup {
  31. self = [super init];
  32. if (self) {
  33. _wrapperLayer = [CALayer new];
  34. [self addSublayer:_wrapperLayer];
  35. DEBUG_Center = [CALayer layer];
  36. DEBUG_Center.bounds = CGRectMake(0, 0, 20, 20);
  37. DEBUG_Center.borderColor = [UIColor blueColor].CGColor;
  38. DEBUG_Center.borderWidth = 2;
  39. DEBUG_Center.masksToBounds = YES;
  40. if (ENABLE_DEBUG_SHAPES) {
  41. [_wrapperLayer addSublayer:DEBUG_Center];
  42. }
  43. self.actions = @{@"hidden" : [NSNull null], @"opacity" : [NSNull null], @"transform" : [NSNull null]};
  44. _wrapperLayer.actions = [self.actions copy];
  45. _timeStretchFactor = @1;
  46. [self commonInitializeWith:layer inLayerGroup:layerGroup];
  47. }
  48. return self;
  49. }
  50. - (void)commonInitializeWith:(LOTLayer *)layer
  51. inLayerGroup:(LOTLayerGroup *)layerGroup {
  52. if (layer == nil) {
  53. return;
  54. }
  55. _layerName = layer.layerName;
  56. if (layer.layerType == LOTLayerTypeImage ||
  57. layer.layerType == LOTLayerTypeSolid ||
  58. layer.layerType == LOTLayerTypePrecomp) {
  59. _wrapperLayer.bounds = CGRectMake(0, 0, layer.layerWidth.floatValue, layer.layerHeight.floatValue);
  60. _wrapperLayer.anchorPoint = CGPointMake(0, 0);
  61. _wrapperLayer.masksToBounds = YES;
  62. DEBUG_Center.position = LOT_RectGetCenterPoint(self.bounds);
  63. }
  64. if (layer.layerType == LOTLayerTypeImage) {
  65. [self _setImageForAsset:layer.imageAsset];
  66. }
  67. _inFrame = [layer.inFrame copy];
  68. _outFrame = [layer.outFrame copy];
  69. _timeStretchFactor = [layer.timeStretch copy];
  70. _transformInterpolator = [LOTTransformInterpolator transformForLayer:layer];
  71. if (layer.parentID != nil) {
  72. NSNumber *parentID = layer.parentID;
  73. LOTTransformInterpolator *childInterpolator = _transformInterpolator;
  74. while (parentID != nil) {
  75. LOTLayer *parentModel = [layerGroup layerModelForID:parentID];
  76. LOTTransformInterpolator *interpolator = [LOTTransformInterpolator transformForLayer:parentModel];
  77. childInterpolator.inputNode = interpolator;
  78. childInterpolator = interpolator;
  79. parentID = parentModel.parentID;
  80. }
  81. }
  82. _opacityInterpolator = [[LOTNumberInterpolator alloc] initWithKeyframes:layer.opacity.keyframes];
  83. if (layer.layerType == LOTLayerTypeShape &&
  84. layer.shapes.count) {
  85. [self buildContents:layer.shapes];
  86. }
  87. if (layer.layerType == LOTLayerTypeSolid) {
  88. _wrapperLayer.backgroundColor = layer.solidColor.CGColor;
  89. }
  90. if (layer.masks.count) {
  91. _maskLayer = [[LOTMaskContainer alloc] initWithMasks:layer.masks];
  92. _wrapperLayer.mask = _maskLayer;
  93. }
  94. NSMutableDictionary *interpolators = [NSMutableDictionary dictionary];
  95. interpolators[@"Opacity"] = _opacityInterpolator;
  96. interpolators[@"Anchor Point"] = _transformInterpolator.anchorInterpolator;
  97. interpolators[@"Scale"] = _transformInterpolator.scaleInterpolator;
  98. interpolators[@"Rotation"] = _transformInterpolator.rotationInterpolator;
  99. if (_transformInterpolator.positionXInterpolator &&
  100. _transformInterpolator.positionYInterpolator) {
  101. interpolators[@"X Position"] = _transformInterpolator.positionXInterpolator;
  102. interpolators[@"Y Position"] = _transformInterpolator.positionYInterpolator;
  103. } else if (_transformInterpolator.positionInterpolator) {
  104. interpolators[@"Position"] = _transformInterpolator.positionInterpolator;
  105. }
  106. // Deprecated
  107. interpolators[@"Transform.Opacity"] = _opacityInterpolator;
  108. interpolators[@"Transform.Anchor Point"] = _transformInterpolator.anchorInterpolator;
  109. interpolators[@"Transform.Scale"] = _transformInterpolator.scaleInterpolator;
  110. interpolators[@"Transform.Rotation"] = _transformInterpolator.rotationInterpolator;
  111. if (_transformInterpolator.positionXInterpolator &&
  112. _transformInterpolator.positionYInterpolator) {
  113. interpolators[@"Transform.X Position"] = _transformInterpolator.positionXInterpolator;
  114. interpolators[@"Transform.Y Position"] = _transformInterpolator.positionYInterpolator;
  115. } else if (_transformInterpolator.positionInterpolator) {
  116. interpolators[@"Transform.Position"] = _transformInterpolator.positionInterpolator;
  117. }
  118. _valueInterpolators = interpolators;
  119. }
  120. - (void)buildContents:(NSArray *)contents {
  121. _contentsGroup = [[LOTRenderGroup alloc] initWithInputNode:nil contents:contents keyname:_layerName];
  122. [_wrapperLayer addSublayer:_contentsGroup.containerLayer];
  123. }
  124. #if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
  125. - (void)_setImageForAsset:(LOTAsset *)asset {
  126. if (asset.imageName) {
  127. UIImage *image;
  128. if ([asset.imageName hasPrefix:@"data:"]) {
  129. // Contents look like a data: URL. Ignore asset.imageDirectory and simply load the image directly.
  130. NSURL *imageUrl = [NSURL URLWithString:asset.imageName];
  131. NSData *imageData = [NSData dataWithContentsOfURL:imageUrl];
  132. image = [UIImage imageWithData:imageData];
  133. } else if (asset.rootDirectory.length > 0) {
  134. NSString *rootDirectory = asset.rootDirectory;
  135. if (asset.imageDirectory.length > 0) {
  136. rootDirectory = [rootDirectory stringByAppendingPathComponent:asset.imageDirectory];
  137. }
  138. NSString *imagePath = [rootDirectory stringByAppendingPathComponent:asset.imageName];
  139. id<LOTImageCache> imageCache = [LOTCacheProvider imageCache];
  140. if (imageCache) {
  141. image = [imageCache imageForKey:imagePath];
  142. if (!image) {
  143. image = [UIImage imageWithContentsOfFile:imagePath];
  144. [imageCache setImage:image forKey:imagePath];
  145. }
  146. } else {
  147. image = [UIImage imageWithContentsOfFile:imagePath];
  148. }
  149. } else {
  150. NSString *imagePath = [asset.assetBundle pathForResource:asset.imageName ofType:nil];
  151. image = [UIImage imageWithContentsOfFile:imagePath];
  152. }
  153. //try loading from asset catalogue instead if all else fails
  154. if (!image) {
  155. image = [UIImage imageNamed:asset.imageName inBundle: asset.assetBundle compatibleWithTraitCollection:nil];
  156. }
  157. if (image) {
  158. _wrapperLayer.contents = (__bridge id _Nullable)(image.CGImage);
  159. } else {
  160. NSLog(@"%s: Warn: image not found: %@", __PRETTY_FUNCTION__, asset.imageName);
  161. }
  162. }
  163. }
  164. #else
  165. - (void)_setImageForAsset:(LOTAsset *)asset {
  166. if (asset.imageName) {
  167. NSArray *components = [asset.imageName componentsSeparatedByString:@"."];
  168. NSImage *image = [NSImage imageNamed:components.firstObject];
  169. if (image == nil) {
  170. if (asset.rootDirectory.length > 0 && asset.imageDirectory.length > 0) {
  171. NSString *imagePath = [[asset.rootDirectory stringByAppendingPathComponent:asset.imageDirectory] stringByAppendingPathComponent:asset.imageName];
  172. image = [[NSImage alloc] initWithContentsOfFile:imagePath];
  173. }
  174. }
  175. if (image) {
  176. NSWindow *window = [NSApp mainWindow];
  177. CGFloat desiredScaleFactor = [window backingScaleFactor];
  178. CGFloat actualScaleFactor = [image recommendedLayerContentsScale:desiredScaleFactor];
  179. id layerContents = [image layerContentsForContentsScale:actualScaleFactor];
  180. _wrapperLayer.contents = layerContents;
  181. }
  182. }
  183. }
  184. #endif
  185. // MARK - Animation
  186. + (BOOL)needsDisplayForKey:(NSString *)key {
  187. if ([key isEqualToString:@"currentFrame"]) {
  188. return YES;
  189. }
  190. return [super needsDisplayForKey:key];
  191. }
  192. - (id<CAAction>)actionForKey:(NSString *)event {
  193. if ([event isEqualToString:@"currentFrame"]) {
  194. CABasicAnimation *theAnimation = [CABasicAnimation
  195. animationWithKeyPath:event];
  196. theAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
  197. theAnimation.fromValue = [[self presentationLayer] valueForKey:event];
  198. return theAnimation;
  199. }
  200. return [super actionForKey:event];
  201. }
  202. - (id)initWithLayer:(id)layer {
  203. if (self = [super initWithLayer:layer]) {
  204. if ([layer isKindOfClass:[LOTLayerContainer class]]) {
  205. LOTLayerContainer *other = (LOTLayerContainer *)layer;
  206. self.currentFrame = [other.currentFrame copy];
  207. }
  208. }
  209. return self;
  210. }
  211. - (void)display {
  212. @synchronized(self) {
  213. LOTLayerContainer *presentation = self;
  214. if (self.animationKeys.count &&
  215. self.presentationLayer) {
  216. presentation = (LOTLayerContainer *)self.presentationLayer;
  217. }
  218. [self displayWithFrame:presentation.currentFrame];
  219. }
  220. }
  221. - (void)displayWithFrame:(NSNumber *)frame {
  222. [self displayWithFrame:frame forceUpdate:NO];
  223. }
  224. - (void)displayWithFrame:(NSNumber *)frame forceUpdate:(BOOL)forceUpdate {
  225. NSNumber *newFrame = @(frame.floatValue / self.timeStretchFactor.floatValue);
  226. if (ENABLE_DEBUG_LOGGING) NSLog(@"View %@ Displaying Frame %@, with local time %@", self, frame, newFrame);
  227. BOOL hidden = NO;
  228. if (_inFrame && _outFrame) {
  229. hidden = (frame.floatValue < _inFrame.floatValue ||
  230. frame.floatValue > _outFrame.floatValue);
  231. }
  232. self.hidden = hidden;
  233. if (hidden) {
  234. return;
  235. }
  236. if (_opacityInterpolator && [_opacityInterpolator hasUpdateForFrame:newFrame]) {
  237. self.opacity = [_opacityInterpolator floatValueForFrame:newFrame];
  238. }
  239. if (_transformInterpolator && [_transformInterpolator hasUpdateForFrame:newFrame]) {
  240. _wrapperLayer.transform = [_transformInterpolator transformForFrame:newFrame];
  241. }
  242. [_contentsGroup updateWithFrame:newFrame withModifierBlock:nil forceLocalUpdate:forceUpdate];
  243. _maskLayer.currentFrame = newFrame;
  244. }
  245. - (void)setViewportBounds:(CGRect)viewportBounds {
  246. _viewportBounds = viewportBounds;
  247. if (_maskLayer) {
  248. CGPoint center = LOT_RectGetCenterPoint(viewportBounds);
  249. viewportBounds.origin = CGPointMake(-center.x, -center.y);
  250. _maskLayer.bounds = viewportBounds;
  251. }
  252. }
  253. - (void)searchNodesForKeypath:(LOTKeypath * _Nonnull)keypath {
  254. if (_contentsGroup == nil && [keypath pushKey:self.layerName]) {
  255. // Matches self.
  256. if ([keypath pushKey:@"Transform"]) {
  257. // Is a transform node, check interpolators
  258. LOTValueInterpolator *interpolator = _valueInterpolators[keypath.currentKey];
  259. if (interpolator) {
  260. // We have a match!
  261. [keypath pushKey:keypath.currentKey];
  262. [keypath addSearchResultForCurrentPath:_wrapperLayer];
  263. [keypath popKey];
  264. }
  265. if (keypath.endOfKeypath) {
  266. [keypath addSearchResultForCurrentPath:_wrapperLayer];
  267. }
  268. [keypath popKey];
  269. }
  270. if (keypath.endOfKeypath) {
  271. [keypath addSearchResultForCurrentPath:_wrapperLayer];
  272. }
  273. [keypath popKey];
  274. }
  275. [_contentsGroup searchNodesForKeypath:keypath];
  276. }
  277. - (void)setValueDelegate:(id<LOTValueDelegate> _Nonnull)delegate
  278. forKeypath:(LOTKeypath * _Nonnull)keypath {
  279. if ([keypath pushKey:self.layerName]) {
  280. // Matches self.
  281. if ([keypath pushKey:@"Transform"]) {
  282. // Is a transform node, check interpolators
  283. LOTValueInterpolator *interpolator = _valueInterpolators[keypath.currentKey];
  284. if (interpolator) {
  285. // We have a match!
  286. [interpolator setValueDelegate:delegate];
  287. }
  288. [keypath popKey];
  289. }
  290. [keypath popKey];
  291. }
  292. [_contentsGroup setValueDelegate:delegate forKeypath:keypath];
  293. }
  294. @end