You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
329 lines
8.9 KiB
329 lines
8.9 KiB
/** |
|
Copyright (c) 2014-present, Facebook, Inc. |
|
All rights reserved. |
|
|
|
This source code is licensed under the BSD-style license found in the |
|
LICENSE file in the root directory of this source tree. An additional grant |
|
of patent rights can be found in the PATENTS file in the same directory. |
|
*/ |
|
|
|
#import "POPAnimationRuntime.h" |
|
|
|
#import <objc/objc.h> |
|
|
|
#import <QuartzCore/QuartzCore.h> |
|
|
|
#if TARGET_OS_IPHONE |
|
#import <UIKit/UIKit.h> |
|
#endif |
|
|
|
#import "POPCGUtils.h" |
|
#import "POPDefines.h" |
|
#import "POPGeometry.h" |
|
#import "POPVector.h" |
|
|
|
static Boolean pointerEqual(const void *ptr1, const void *ptr2) { |
|
return ptr1 == ptr2; |
|
} |
|
|
|
static CFHashCode pointerHash(const void *ptr) { |
|
return (CFHashCode)(ptr); |
|
} |
|
|
|
CFMutableDictionaryRef POPDictionaryCreateMutableWeakPointerToWeakPointer(NSUInteger capacity) |
|
{ |
|
CFDictionaryKeyCallBacks kcb = kCFTypeDictionaryKeyCallBacks; |
|
|
|
// weak, pointer keys |
|
kcb.retain = NULL; |
|
kcb.release = NULL; |
|
kcb.equal = pointerEqual; |
|
kcb.hash = pointerHash; |
|
|
|
CFDictionaryValueCallBacks vcb = kCFTypeDictionaryValueCallBacks; |
|
|
|
// weak, pointer values |
|
vcb.retain = NULL; |
|
vcb.release = NULL; |
|
vcb.equal = pointerEqual; |
|
|
|
return CFDictionaryCreateMutable(NULL, capacity, &kcb, &vcb); |
|
} |
|
|
|
CFMutableDictionaryRef POPDictionaryCreateMutableWeakPointerToStrongObject(NSUInteger capacity) |
|
{ |
|
CFDictionaryKeyCallBacks kcb = kCFTypeDictionaryKeyCallBacks; |
|
|
|
// weak, pointer keys |
|
kcb.retain = NULL; |
|
kcb.release = NULL; |
|
kcb.equal = pointerEqual; |
|
kcb.hash = pointerHash; |
|
|
|
// strong, object values |
|
CFDictionaryValueCallBacks vcb = kCFTypeDictionaryValueCallBacks; |
|
|
|
return CFDictionaryCreateMutable(NULL, capacity, &kcb, &vcb); |
|
} |
|
|
|
static bool FBCompareTypeEncoding(const char *objctype, POPValueType type) |
|
{ |
|
switch (type) |
|
{ |
|
case kPOPValueFloat: |
|
return (strcmp(objctype, @encode(float)) == 0 |
|
|| strcmp(objctype, @encode(double)) == 0 |
|
); |
|
|
|
case kPOPValuePoint: |
|
return (strcmp(objctype, @encode(CGPoint)) == 0 |
|
#if !TARGET_OS_IPHONE |
|
|| strcmp(objctype, @encode(NSPoint)) == 0 |
|
#endif |
|
); |
|
|
|
case kPOPValueSize: |
|
return (strcmp(objctype, @encode(CGSize)) == 0 |
|
#if !TARGET_OS_IPHONE |
|
|| strcmp(objctype, @encode(NSSize)) == 0 |
|
#endif |
|
); |
|
|
|
case kPOPValueRect: |
|
return (strcmp(objctype, @encode(CGRect)) == 0 |
|
#if !TARGET_OS_IPHONE |
|
|| strcmp(objctype, @encode(NSRect)) == 0 |
|
#endif |
|
); |
|
case kPOPValueEdgeInsets: |
|
#if TARGET_OS_IPHONE |
|
return strcmp(objctype, @encode(UIEdgeInsets)) == 0; |
|
#else |
|
return false; |
|
#endif |
|
|
|
case kPOPValueAffineTransform: |
|
return strcmp(objctype, @encode(CGAffineTransform)) == 0; |
|
|
|
case kPOPValueTransform: |
|
return strcmp(objctype, @encode(CATransform3D)) == 0; |
|
|
|
case kPOPValueRange: |
|
return strcmp(objctype, @encode(CFRange)) == 0 |
|
|| strcmp(objctype, @encode (NSRange)) == 0; |
|
|
|
case kPOPValueInteger: |
|
return (strcmp(objctype, @encode(int)) == 0 |
|
|| strcmp(objctype, @encode(unsigned int)) == 0 |
|
|| strcmp(objctype, @encode(short)) == 0 |
|
|| strcmp(objctype, @encode(unsigned short)) == 0 |
|
|| strcmp(objctype, @encode(long)) == 0 |
|
|| strcmp(objctype, @encode(unsigned long)) == 0 |
|
|| strcmp(objctype, @encode(long long)) == 0 |
|
|| strcmp(objctype, @encode(unsigned long long)) == 0 |
|
); |
|
|
|
case kPOPValueSCNVector3: |
|
#if SCENEKIT_SDK_AVAILABLE |
|
return strcmp(objctype, @encode(SCNVector3)) == 0; |
|
#else |
|
return false; |
|
#endif |
|
|
|
case kPOPValueSCNVector4: |
|
#if SCENEKIT_SDK_AVAILABLE |
|
return strcmp(objctype, @encode(SCNVector4)) == 0; |
|
#else |
|
return false; |
|
#endif |
|
|
|
default: |
|
return false; |
|
} |
|
} |
|
|
|
POPValueType POPSelectValueType(const char *objctype, const POPValueType *types, size_t length) |
|
{ |
|
if (NULL != objctype) { |
|
for (size_t idx = 0; idx < length; idx++) { |
|
if (FBCompareTypeEncoding(objctype, types[idx])) |
|
return types[idx]; |
|
} |
|
} |
|
return kPOPValueUnknown; |
|
} |
|
|
|
POPValueType POPSelectValueType(id obj, const POPValueType *types, size_t length) |
|
{ |
|
if ([obj isKindOfClass:[NSValue class]]) { |
|
return POPSelectValueType([obj objCType], types, length); |
|
} else if (NULL != POPCGColorWithColor(obj)) { |
|
return kPOPValueColor; |
|
} |
|
return kPOPValueUnknown; |
|
} |
|
|
|
const POPValueType kPOPAnimatableAllTypes[12] = {kPOPValueInteger, kPOPValueFloat, kPOPValuePoint, kPOPValueSize, kPOPValueRect, kPOPValueEdgeInsets, kPOPValueAffineTransform, kPOPValueTransform, kPOPValueRange, kPOPValueColor, kPOPValueSCNVector3, kPOPValueSCNVector4}; |
|
|
|
const POPValueType kPOPAnimatableSupportTypes[10] = {kPOPValueInteger, kPOPValueFloat, kPOPValuePoint, kPOPValueSize, kPOPValueRect, kPOPValueEdgeInsets, kPOPValueColor, kPOPValueSCNVector3, kPOPValueSCNVector4}; |
|
|
|
NSString *POPValueTypeToString(POPValueType t) |
|
{ |
|
switch (t) { |
|
case kPOPValueUnknown: |
|
return @"unknown"; |
|
case kPOPValueInteger: |
|
return @"int"; |
|
case kPOPValueFloat: |
|
return @"CGFloat"; |
|
case kPOPValuePoint: |
|
return @"CGPoint"; |
|
case kPOPValueSize: |
|
return @"CGSize"; |
|
case kPOPValueRect: |
|
return @"CGRect"; |
|
case kPOPValueEdgeInsets: |
|
return @"UIEdgeInsets"; |
|
case kPOPValueAffineTransform: |
|
return @"CGAffineTransform"; |
|
case kPOPValueTransform: |
|
return @"CATransform3D"; |
|
case kPOPValueRange: |
|
return @"CFRange"; |
|
case kPOPValueColor: |
|
return @"CGColorRef"; |
|
case kPOPValueSCNVector3: |
|
return @"SCNVector3"; |
|
case kPOPValueSCNVector4: |
|
return @"SCNVector4"; |
|
default: |
|
return nil; |
|
} |
|
} |
|
|
|
id POPBox(VectorConstRef vec, POPValueType type, bool force) |
|
{ |
|
if (NULL == vec) |
|
return nil; |
|
|
|
switch (type) { |
|
case kPOPValueInteger: |
|
case kPOPValueFloat: |
|
return @(vec->data()[0]); |
|
break; |
|
case kPOPValuePoint: |
|
return [NSValue valueWithCGPoint:vec->cg_point()]; |
|
break; |
|
case kPOPValueSize: |
|
return [NSValue valueWithCGSize:vec->cg_size()]; |
|
break; |
|
case kPOPValueRect: |
|
return [NSValue valueWithCGRect:vec->cg_rect()]; |
|
break; |
|
#if TARGET_OS_IPHONE |
|
case kPOPValueEdgeInsets: |
|
return [NSValue valueWithUIEdgeInsets:vec->ui_edge_insets()]; |
|
break; |
|
#endif |
|
case kPOPValueColor: { |
|
return (__bridge_transfer id)vec->cg_color(); |
|
break; |
|
} |
|
#if SCENEKIT_SDK_AVAILABLE |
|
case kPOPValueSCNVector3: { |
|
return [NSValue valueWithSCNVector3:vec->scn_vector3()]; |
|
break; |
|
} |
|
case kPOPValueSCNVector4: { |
|
return [NSValue valueWithSCNVector4:vec->scn_vector4()]; |
|
break; |
|
} |
|
#endif |
|
default: |
|
return force ? [NSValue valueWithCGPoint:vec->cg_point()] : nil; |
|
break; |
|
} |
|
} |
|
|
|
static VectorRef vectorize(id value, POPValueType type) |
|
{ |
|
Vector *vec = NULL; |
|
|
|
switch (type) { |
|
case kPOPValueInteger: |
|
case kPOPValueFloat: |
|
#if CGFLOAT_IS_DOUBLE |
|
vec = Vector::new_cg_float([value doubleValue]); |
|
#else |
|
vec = Vector::new_cg_float([value floatValue]); |
|
#endif |
|
break; |
|
case kPOPValuePoint: |
|
vec = Vector::new_cg_point([value CGPointValue]); |
|
break; |
|
case kPOPValueSize: |
|
vec = Vector::new_cg_size([value CGSizeValue]); |
|
break; |
|
case kPOPValueRect: |
|
vec = Vector::new_cg_rect([value CGRectValue]); |
|
break; |
|
#if TARGET_OS_IPHONE |
|
case kPOPValueEdgeInsets: |
|
vec = Vector::new_ui_edge_insets([value UIEdgeInsetsValue]); |
|
break; |
|
#endif |
|
case kPOPValueAffineTransform: |
|
vec = Vector::new_cg_affine_transform([value CGAffineTransformValue]); |
|
break; |
|
case kPOPValueColor: |
|
vec = Vector::new_cg_color(POPCGColorWithColor(value)); |
|
break; |
|
#if SCENEKIT_SDK_AVAILABLE |
|
case kPOPValueSCNVector3: |
|
vec = Vector::new_scn_vector3([value SCNVector3Value]); |
|
break; |
|
case kPOPValueSCNVector4: |
|
vec = Vector::new_scn_vector4([value SCNVector4Value]); |
|
break; |
|
#endif |
|
default: |
|
break; |
|
} |
|
|
|
return VectorRef(vec); |
|
} |
|
|
|
VectorRef POPUnbox(id value, POPValueType &animationType, NSUInteger &count, bool validate) |
|
{ |
|
if (nil == value) { |
|
count = 0; |
|
return VectorRef(NULL); |
|
} |
|
|
|
// determine type of value |
|
POPValueType valueType = POPSelectValueType(value, kPOPAnimatableSupportTypes, POP_ARRAY_COUNT(kPOPAnimatableSupportTypes)); |
|
|
|
// handle unknown types |
|
if (kPOPValueUnknown == valueType) { |
|
NSString *valueDesc = [[value class] description]; |
|
[NSException raise:@"Unsuported value" format:@"Animating %@ values is not supported", valueDesc]; |
|
} |
|
|
|
// vectorize |
|
VectorRef vec = vectorize(value, valueType); |
|
|
|
if (kPOPValueUnknown == animationType || 0 == count) { |
|
// update animation type based on value type |
|
animationType = valueType; |
|
if (NULL != vec) { |
|
count = vec->size(); |
|
} |
|
} else if (validate) { |
|
// allow for mismatched types, so long as vector size matches |
|
if (count != vec->size()) { |
|
[NSException raise:@"Invalid value" format:@"%@ should be of type %@", value, POPValueTypeToString(animationType)]; |
|
} |
|
} |
|
|
|
return vec; |
|
}
|
|
|