forked from TextureGroup/Texture
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path_ASCoreAnimationExtras.mm
More file actions
186 lines (164 loc) · 7.86 KB
/
Copy path_ASCoreAnimationExtras.mm
File metadata and controls
186 lines (164 loc) · 7.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
//
// _ASCoreAnimationExtras.mm
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/_ASCoreAnimationExtras.h>
#import <AsyncDisplayKit/ASEqualityHelpers.h>
void ASDisplayNodeSetupLayerContentsWithResizableImage(CALayer *layer, UIImage *image)
{
ASDisplayNodeSetResizableContents(layer, image);
}
void ASDisplayNodeSetResizableContents(id<ASResizableContents> obj, UIImage *image)
{
// FIXME (https://github.com/TextureGroup/Texture/issues/1046): This method does not currently handle UIImageResizingModeTile, which is the default.
// See also https://developer.apple.com/documentation/uikit/uiimage/1624157-resizingmode?language=objc
// I'm not sure of a way to use CALayer directly to perform such tiling on the GPU, though the stretch is handled by the GPU,
// and CALayer.h documents the fact that contentsCenter is used to stretch the pixels.
if (image) {
ASDisplayNodeCAssert(image.resizingMode == UIImageResizingModeStretch || UIEdgeInsetsEqualToEdgeInsets(image.capInsets, UIEdgeInsetsZero),
@"Image insets must be all-zero or resizingMode has to be UIImageResizingModeStretch. XCode assets default value is UIImageResizingModeTile which is not supported by Texture because of GPU-accelerated CALayer features.");
// Image may not actually be stretchable in one or both dimensions; this is handled
obj.contents = (id)[image CGImage];
obj.contentsScale = [image scale];
obj.rasterizationScale = [image scale];
CGSize imageSize = [image size];
UIEdgeInsets insets = [image capInsets];
// These are lifted from what UIImageView does by experimentation. Without these exact values, the stretching is slightly off.
const CGFloat halfPixelFudge = 0.49f;
const CGFloat otherPixelFudge = 0.02f;
// Convert to unit coordinates for the contentsCenter property.
CGRect contentsCenter = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f);
if (insets.left > 0 || insets.right > 0) {
contentsCenter.origin.x = ((insets.left + halfPixelFudge) / imageSize.width);
contentsCenter.size.width = (imageSize.width - (insets.left + insets.right + 1.f) + otherPixelFudge) / imageSize.width;
}
if (insets.top > 0 || insets.bottom > 0) {
contentsCenter.origin.y = ((insets.top + halfPixelFudge) / imageSize.height);
contentsCenter.size.height = (imageSize.height - (insets.top + insets.bottom + 1.f) + otherPixelFudge) / imageSize.height;
}
obj.contentsGravity = kCAGravityResize;
obj.contentsCenter = contentsCenter;
} else {
obj.contents = nil;
}
}
struct _UIContentModeStringLUTEntry {
UIViewContentMode contentMode;
NSString *const string;
};
static const _UIContentModeStringLUTEntry *UIContentModeCAGravityLUT(size_t *count)
{
// Initialize this in a function (instead of at file level) to avoid
// startup initialization time.
static const _UIContentModeStringLUTEntry sUIContentModeCAGravityLUT[] = {
{UIViewContentModeScaleToFill, kCAGravityResize},
{UIViewContentModeScaleAspectFit, kCAGravityResizeAspect},
{UIViewContentModeScaleAspectFill, kCAGravityResizeAspectFill},
{UIViewContentModeCenter, kCAGravityCenter},
{UIViewContentModeTop, kCAGravityBottom},
{UIViewContentModeBottom, kCAGravityTop},
{UIViewContentModeLeft, kCAGravityLeft},
{UIViewContentModeRight, kCAGravityRight},
{UIViewContentModeTopLeft, kCAGravityBottomLeft},
{UIViewContentModeTopRight, kCAGravityBottomRight},
{UIViewContentModeBottomLeft, kCAGravityTopLeft},
{UIViewContentModeBottomRight, kCAGravityTopRight},
};
*count = AS_ARRAY_SIZE(sUIContentModeCAGravityLUT);
return sUIContentModeCAGravityLUT;
}
static const _UIContentModeStringLUTEntry *UIContentModeDescriptionLUT(size_t *count)
{
// Initialize this in a function (instead of at file level) to avoid
// startup initialization time.
static const _UIContentModeStringLUTEntry sUIContentModeDescriptionLUT[] = {
{UIViewContentModeScaleToFill, @"scaleToFill"},
{UIViewContentModeScaleAspectFit, @"aspectFit"},
{UIViewContentModeScaleAspectFill, @"aspectFill"},
{UIViewContentModeRedraw, @"redraw"},
{UIViewContentModeCenter, @"center"},
{UIViewContentModeTop, @"top"},
{UIViewContentModeBottom, @"bottom"},
{UIViewContentModeLeft, @"left"},
{UIViewContentModeRight, @"right"},
{UIViewContentModeTopLeft, @"topLeft"},
{UIViewContentModeTopRight, @"topRight"},
{UIViewContentModeBottomLeft, @"bottomLeft"},
{UIViewContentModeBottomRight, @"bottomRight"},
};
*count = AS_ARRAY_SIZE(sUIContentModeDescriptionLUT);
return sUIContentModeDescriptionLUT;
}
NSString *ASDisplayNodeNSStringFromUIContentMode(UIViewContentMode contentMode)
{
size_t lutSize;
const _UIContentModeStringLUTEntry *lut = UIContentModeDescriptionLUT(&lutSize);
for (size_t i = 0; i < lutSize; ++i) {
if (lut[i].contentMode == contentMode) {
return lut[i].string;
}
}
return [NSString stringWithFormat:@"%d", (int)contentMode];
}
UIViewContentMode ASDisplayNodeUIContentModeFromNSString(NSString *string)
{
size_t lutSize;
const _UIContentModeStringLUTEntry *lut = UIContentModeDescriptionLUT(&lutSize);
for (size_t i = 0; i < lutSize; ++i) {
if (ASObjectIsEqual(lut[i].string, string)) {
return lut[i].contentMode;
}
}
return UIViewContentModeScaleToFill;
}
NSString *const ASDisplayNodeCAContentsGravityFromUIContentMode(UIViewContentMode contentMode)
{
size_t lutSize;
const _UIContentModeStringLUTEntry *lut = UIContentModeCAGravityLUT(&lutSize);
for (size_t i = 0; i < lutSize; ++i) {
if (lut[i].contentMode == contentMode) {
return lut[i].string;
}
}
ASDisplayNodeCAssert(contentMode == UIViewContentModeRedraw, @"Encountered an unknown contentMode %ld. Is this a new version of iOS?", (long)contentMode);
// Redraw is ok to return nil.
return nil;
}
#define ContentModeCacheSize 10
UIViewContentMode ASDisplayNodeUIContentModeFromCAContentsGravity(NSString *const contentsGravity)
{
static int currentCacheIndex = 0;
static NSMutableArray *cachedStrings = [NSMutableArray arrayWithCapacity:ContentModeCacheSize];
static UIViewContentMode cachedModes[ContentModeCacheSize] = {};
NSInteger foundCacheIndex = [cachedStrings indexOfObjectIdenticalTo:contentsGravity];
if (foundCacheIndex != NSNotFound && foundCacheIndex < ContentModeCacheSize) {
return cachedModes[foundCacheIndex];
}
size_t lutSize;
const _UIContentModeStringLUTEntry *lut = UIContentModeCAGravityLUT(&lutSize);
for (size_t i = 0; i < lutSize; ++i) {
if (ASObjectIsEqual(lut[i].string, contentsGravity)) {
UIViewContentMode foundContentMode = lut[i].contentMode;
if (currentCacheIndex < ContentModeCacheSize) {
// Cache the input value. This is almost always a different pointer than in our LUT and will frequently
// be the same value for an overwhelming majority of inputs.
[cachedStrings addObject:contentsGravity];
cachedModes[currentCacheIndex] = foundContentMode;
currentCacheIndex++;
}
return foundContentMode;
}
}
ASDisplayNodeCAssert(contentsGravity, @"Encountered an unknown contentsGravity \"%@\". Is this a new version of iOS?", contentsGravity);
ASDisplayNodeCAssert(!contentsGravity, @"You passed nil to ASDisplayNodeUIContentModeFromCAContentsGravity. We're falling back to resize, but this is probably a bug.");
// If asserts disabled, fall back to this
return UIViewContentModeScaleToFill;
}
BOOL ASDisplayNodeLayerHasAnimations(CALayer *layer)
{
return (layer.animationKeys.count != 0);
}