Home asdisplaynode-coreanimation-extras
Post
Cancel

asdisplaynode-coreanimation-extras

_ASCoreAnimationExtras.mm

ASDisplayNodeSetResizableContents

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
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;
  }
}

_UIContentModeStringLUTEntry

1
2
3
4
struct _UIContentModeStringLUTEntry {
  UIViewContentMode contentMode;
  NSString *const string;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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 = sizeof(sUIContentModeCAGravityLUT) / sizeof(sUIContentModeCAGravityLUT[0]);
  return sUIContentModeCAGravityLUT;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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 = sizeof(sUIContentModeDescriptionLUT) / sizeof(sUIContentModeDescriptionLUT[0]);
  return sUIContentModeDescriptionLUT;
}
1
2
3
4
5
6
7
8
9
10
11
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];
}
1
2
3
4
5
6
7
8
9
10
11
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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
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;
}
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
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;
}
1
2
3
4
BOOL ASDisplayNodeLayerHasAnimations(CALayer *layer)
{
  return (layer.animationKeys.count != 0);
}
This post is licensed under CC BY 4.0 by the author.