Skip to content

Commit

Permalink
Optimize drawing code + add examples how to round corners (#996)
Browse files Browse the repository at this point in the history
* Use CoreGraphics for drawing and cropping of node content

* Smaller fixes
  • Loading branch information
maicki authored and nguyenhuy committed Jul 26, 2018
1 parent 4880b54 commit eb4c21c
Show file tree
Hide file tree
Showing 16 changed files with 250 additions and 109 deletions.
16 changes: 8 additions & 8 deletions AsyncDisplayKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@
509E68631B3AEDB4009B9150 /* ASCollectionViewLayoutController.h in Headers */ = {isa = PBXBuildFile; fileRef = 205F0E1B1B373A2C007741D0 /* ASCollectionViewLayoutController.h */; };
509E68641B3AEDB7009B9150 /* ASCollectionViewLayoutController.m in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E1C1B373A2C007741D0 /* ASCollectionViewLayoutController.m */; };
509E68651B3AEDC5009B9150 /* CoreGraphics+ASConvenience.h in Headers */ = {isa = PBXBuildFile; fileRef = 205F0E1F1B376416007741D0 /* CoreGraphics+ASConvenience.h */; settings = {ATTRIBUTES = (Public, ); }; };
509E68661B3AEDD7009B9150 /* CoreGraphics+ASConvenience.m in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E201B376416007741D0 /* CoreGraphics+ASConvenience.m */; };
509E68661B3AEDD7009B9150 /* CoreGraphics+ASConvenience.mm in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E201B376416007741D0 /* CoreGraphics+ASConvenience.mm */; };
636EA1A41C7FF4EC00EE152F /* NSArray+Diffing.mm in Sources */ = {isa = PBXBuildFile; fileRef = DBC452DA1C5BF64600B16017 /* NSArray+Diffing.mm */; };
636EA1A51C7FF4EF00EE152F /* ASDefaultPlayButton.m in Sources */ = {isa = PBXBuildFile; fileRef = AEB7B0191C5962EA00662EF4 /* ASDefaultPlayButton.m */; };
680346941CE4052A0009FEB4 /* ASNavigationController.h in Headers */ = {isa = PBXBuildFile; fileRef = 68FC85DC1CE29AB700EDD713 /* ASNavigationController.h */; settings = {ATTRIBUTES = (Public, ); }; };
Expand Down Expand Up @@ -176,7 +176,7 @@
7AB338671C55B3460055FDE8 /* ASRelativeLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A06A7391C35F08800FE8DAA /* ASRelativeLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; };
7AB338691C55B97B0055FDE8 /* ASRelativeLayoutSpecSnapshotTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7AB338681C55B97B0055FDE8 /* ASRelativeLayoutSpecSnapshotTests.mm */; };
8021EC1D1D2B00B100799119 /* UIImage+ASConvenience.h in Headers */ = {isa = PBXBuildFile; fileRef = 8021EC1A1D2B00B100799119 /* UIImage+ASConvenience.h */; settings = {ATTRIBUTES = (Public, ); }; };
8021EC1F1D2B00B100799119 /* UIImage+ASConvenience.m in Sources */ = {isa = PBXBuildFile; fileRef = 8021EC1B1D2B00B100799119 /* UIImage+ASConvenience.m */; };
8021EC1F1D2B00B100799119 /* UIImage+ASConvenience.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8021EC1B1D2B00B100799119 /* UIImage+ASConvenience.mm */; };
81E95C141D62639600336598 /* ASTextNodeSnapshotTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 81E95C131D62639600336598 /* ASTextNodeSnapshotTests.m */; };
83A7D95B1D44547700BF333E /* ASWeakMap.m in Sources */ = {isa = PBXBuildFile; fileRef = 83A7D9591D44542100BF333E /* ASWeakMap.m */; };
83A7D95C1D44548100BF333E /* ASWeakMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 83A7D9581D44542100BF333E /* ASWeakMap.h */; settings = {ATTRIBUTES = (Private, ); }; };
Expand Down Expand Up @@ -621,7 +621,7 @@
205F0E1B1B373A2C007741D0 /* ASCollectionViewLayoutController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionViewLayoutController.h; sourceTree = "<group>"; };
205F0E1C1B373A2C007741D0 /* ASCollectionViewLayoutController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionViewLayoutController.m; sourceTree = "<group>"; };
205F0E1F1B376416007741D0 /* CoreGraphics+ASConvenience.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CoreGraphics+ASConvenience.h"; sourceTree = "<group>"; };
205F0E201B376416007741D0 /* CoreGraphics+ASConvenience.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "CoreGraphics+ASConvenience.m"; sourceTree = "<group>"; };
205F0E201B376416007741D0 /* CoreGraphics+ASConvenience.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "CoreGraphics+ASConvenience.mm"; sourceTree = "<group>"; };
242995D21B29743C00090100 /* ASBasicImageDownloaderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASBasicImageDownloaderTests.m; sourceTree = "<group>"; };
2538B6F21BC5D2A2003CA0B4 /* ASCollectionViewFlowLayoutInspectorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = ASCollectionViewFlowLayoutInspectorTests.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
254C6B511BF8FE6D003EC431 /* ASTextKitTruncationTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASTextKitTruncationTests.mm; sourceTree = "<group>"; };
Expand Down Expand Up @@ -734,7 +734,7 @@
7A06A7391C35F08800FE8DAA /* ASRelativeLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASRelativeLayoutSpec.h; sourceTree = "<group>"; };
7AB338681C55B97B0055FDE8 /* ASRelativeLayoutSpecSnapshotTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASRelativeLayoutSpecSnapshotTests.mm; sourceTree = "<group>"; };
8021EC1A1D2B00B100799119 /* UIImage+ASConvenience.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+ASConvenience.h"; sourceTree = "<group>"; };
8021EC1B1D2B00B100799119 /* UIImage+ASConvenience.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+ASConvenience.m"; sourceTree = "<group>"; };
8021EC1B1D2B00B100799119 /* UIImage+ASConvenience.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "UIImage+ASConvenience.mm"; sourceTree = "<group>"; };
81E95C131D62639600336598 /* ASTextNodeSnapshotTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTextNodeSnapshotTests.m; sourceTree = "<group>"; };
81EE384D1C8E94F000456208 /* ASRunLoopQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASRunLoopQueue.h; path = ../ASRunLoopQueue.h; sourceTree = "<group>"; };
81EE384E1C8E94F000456208 /* ASRunLoopQueue.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASRunLoopQueue.mm; path = ../ASRunLoopQueue.mm; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1234,7 +1234,7 @@
68FC85E81CE29C7D00EDD713 /* ASVisibilityProtocols.m */,
6BDC61F51978FEA400E50D21 /* AsyncDisplayKit.h */,
8021EC1A1D2B00B100799119 /* UIImage+ASConvenience.h */,
8021EC1B1D2B00B100799119 /* UIImage+ASConvenience.m */,
8021EC1B1D2B00B100799119 /* UIImage+ASConvenience.mm */,
CC55A70B1E529FA200594372 /* UIResponder+AsyncDisplayKit.h */,
CC55A70C1E529FA200594372 /* UIResponder+AsyncDisplayKit.m */,
);
Expand Down Expand Up @@ -1422,7 +1422,7 @@
CC3B20871C3F7A5400798563 /* ASWeakSet.h */,
CC3B20881C3F7A5400798563 /* ASWeakSet.m */,
205F0E1F1B376416007741D0 /* CoreGraphics+ASConvenience.h */,
205F0E201B376416007741D0 /* CoreGraphics+ASConvenience.m */,
205F0E201B376416007741D0 /* CoreGraphics+ASConvenience.mm */,
DBC452D91C5BF64600B16017 /* NSArray+Diffing.h */,
DBC452DA1C5BF64600B16017 /* NSArray+Diffing.mm */,
CC4981BA1D1C7F65004E13CC /* NSIndexSet+ASHelpers.h */,
Expand Down Expand Up @@ -2405,7 +2405,7 @@
CCA282C51E9EAE630037E8B7 /* ASLayerBackingTipProvider.m in Sources */,
509E68641B3AEDB7009B9150 /* ASCollectionViewLayoutController.m in Sources */,
B35061F91B010EFD0018CF92 /* ASControlNode.mm in Sources */,
8021EC1F1D2B00B100799119 /* UIImage+ASConvenience.m in Sources */,
8021EC1F1D2B00B100799119 /* UIImage+ASConvenience.mm in Sources */,
CCAA0B80206ADBF30057B336 /* ASRecursiveUnfairLock.m in Sources */,
CCBDDD0620C62A2D00CBA922 /* ASMainThreadDeallocation.mm in Sources */,
B35062181B010EFD0018CF92 /* ASDataController.mm in Sources */,
Expand Down Expand Up @@ -2504,7 +2504,7 @@
6959433F1D70815300B0EE1F /* ASDisplayNodeLayout.mm in Sources */,
68355B3E1CB57A60001D4E68 /* ASPINRemoteImageDownloader.m in Sources */,
CC034A141E649F1300626263 /* AsyncDisplayKit+IGListKitMethods.m in Sources */,
509E68661B3AEDD7009B9150 /* CoreGraphics+ASConvenience.m in Sources */,
509E68661B3AEDD7009B9150 /* CoreGraphics+ASConvenience.mm in Sources */,
254C6B871BF94F8A003EC431 /* ASTextKitEntityAttribute.m in Sources */,
34566CB31BC1213700715E6B /* ASPhotosFrameworkImageRequest.m in Sources */,
254C6B831BF94F8A003EC431 /* ASTextKitCoreTextAdditions.m in Sources */,
Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
- Reduced binary size by disabling exception support (which we don't use.) [Adlai Holler](https://github.com/Adlai-Holler)
- Create and set delegate for clip corner layers within ASDisplayNode [Michael Schneider](https://github.com/maicki) [#1029](https://github.com/TextureGroup/Texture/pull/1029)
- Improve locking situation in ASVideoPlayerNode [Michael Schneider](https://github.com/maicki) [#1042](https://github.com/TextureGroup/Texture/pull/1042)

- Optimize drawing code + add examples how to round corners. [Michael Schneider](https://github.com/maicki)

## 2.7
- Fix pager node for interface coalescing. [Max Wang](https://github.com/wsdwsd0829) [#877](https://github.com/TextureGroup/Texture/pull/877)
Expand Down
55 changes: 32 additions & 23 deletions Source/ASDisplayNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
#import <AsyncDisplayKit/ASWeakProxy.h>
#import <AsyncDisplayKit/ASResponderChainEnumerator.h>
#import <AsyncDisplayKit/ASTipsController.h>
#import <AsyncDisplayKit/CoreGraphics+ASConvenience.h>

// Conditionally time these scopes to our debug ivars (only exist in debug/profile builds)
#if TIME_DISPLAYNODE_OPS
Expand Down Expand Up @@ -1508,7 +1509,7 @@ - (void)_pendingNodeDidDisplay:(ASDisplayNode *)node
__instanceLock__.lock();
if (_placeholderLayer.superlayer && !placeholderShouldPersist) {
void (^cleanupBlock)() = ^{
[_placeholderLayer removeFromSuperlayer];
[self->_placeholderLayer removeFromSuperlayer];
};

if (_placeholderFadeDuration > 0.0 && ASInterfaceStateIncludesVisible(self.interfaceState)) {
Expand Down Expand Up @@ -1666,24 +1667,29 @@ - (void)_updateClipCornerLayerContentsWithRadius:(CGFloat)radius backgroundColor
CGSize size = CGSizeMake(radius + 1, radius + 1);
ASGraphicsBeginImageContextWithOptions(size, NO, self.contentsScaleForDisplay);

CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextRef context = UIGraphicsGetCurrentContext();
if (isRight == YES) {
CGContextTranslateCTM(ctx, -radius + 1, 0);
CGContextTranslateCTM(context, -radius + 1, 0);
}
if (isTop == YES) {
CGContextTranslateCTM(ctx, 0, -radius + 1);
CGContextTranslateCTM(context, 0, -radius + 1);
}
UIBezierPath *roundedRect = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, radius * 2, radius * 2) cornerRadius:radius];
[roundedRect setUsesEvenOddFillRule:YES];
[roundedRect appendPath:[UIBezierPath bezierPathWithRect:CGRectMake(-1, -1, radius * 2 + 1, radius * 2 + 1)]];
[backgroundColor setFill];
[roundedRect fill];


CGMutablePathRef roundedPath = CGPathCreateMutable();
CGPathRef addedPath = ASCGRoundedPathCreate(CGRectMake(0, 0, radius * 2, radius * 2), radius);
CGPathAddPath(roundedPath, NULL, addedPath);
CGPathAddRect(roundedPath, NULL, CGRectMake(-1, -1, radius * 2 + 1, radius * 2 + 1));
CGContextAddPath(context, roundedPath);
CGContextSetFillColorWithColor(context, backgroundColor.CGColor);
CGContextEOFillPath(context);

// No lock needed, as _clipCornerLayers is only modified on the main thread.
CALayer *clipCornerLayer = _clipCornerLayers[idx];
clipCornerLayer.contents = (id)(ASGraphicsGetImageAndEndCurrentContext().CGImage);
clipCornerLayer.bounds = CGRectMake(0.0, 0.0, size.width, size.height);
clipCornerLayer.anchorPoint = CGPointMake(isRight ? 1.0 : 0.0, isTop ? 1.0 : 0.0);
_clipCornerLayers[idx].contents = (id)(ASGraphicsGetImageAndEndCurrentContext().CGImage);
_clipCornerLayers[idx].bounds = CGRectMake(0.0, 0.0, size.width, size.height);
_clipCornerLayers[idx].anchorPoint = CGPointMake(isRight ? 1.0 : 0.0, isTop ? 1.0 : 0.0);

CGPathRelease(addedPath);
CGPathRelease(roundedPath);
}
[self _layoutClipCornersIfNeeded];
});
Expand Down Expand Up @@ -1868,7 +1874,10 @@ - (void)didDisplayAsyncLayer:(_ASDisplayLayer *)layer
[self displayDidFinish];
}

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- (void)displayWillStart {}
#pragma clang diagnostic pop
- (void)displayWillStartAsynchronously:(BOOL)asynchronously
{
ASDisplayNodeAssertMainThread();
Expand Down Expand Up @@ -2979,11 +2988,11 @@ - (void)didExitHierarchy
if (ASInterfaceStateIncludesVisible(self.pendingInterfaceState)) {
void(^exitVisibleInterfaceState)(void) = ^{
// This block intentionally retains self.
__instanceLock__.lock();
unsigned isStillInHierarchy = _flags.isInHierarchy;
BOOL isVisible = ASInterfaceStateIncludesVisible(_pendingInterfaceState);
ASInterfaceState newState = (_pendingInterfaceState & ~ASInterfaceStateVisible);
__instanceLock__.unlock();
self->__instanceLock__.lock();
unsigned isStillInHierarchy = self->_flags.isInHierarchy;
BOOL isVisible = ASInterfaceStateIncludesVisible(self->_pendingInterfaceState);
ASInterfaceState newState = (self->_pendingInterfaceState & ~ASInterfaceStateVisible);
self->__instanceLock__.unlock();
if (!isStillInHierarchy && isVisible) {
#if ENABLE_NEW_EXIT_HIERARCHY_BEHAVIOR
if (![self supportsRangeManagedInterfaceState]) {
Expand Down Expand Up @@ -3142,8 +3151,8 @@ - (void)applyPendingInterfaceState:(ASInterfaceState)newPendingState
[self setDisplaySuspended:YES];
//schedule clear contents on next runloop
dispatch_async(dispatch_get_main_queue(), ^{
ASDN::MutexLocker l(__instanceLock__);
if (ASInterfaceStateIncludesDisplay(_interfaceState) == NO) {
ASDN::MutexLocker l(self->__instanceLock__);
if (ASInterfaceStateIncludesDisplay(self->_interfaceState) == NO) {
[self clearContents];
}
});
Expand All @@ -3160,8 +3169,8 @@ - (void)applyPendingInterfaceState:(ASInterfaceState)newPendingState
[[self asyncLayer] cancelAsyncDisplay];
//schedule clear contents on next runloop
dispatch_async(dispatch_get_main_queue(), ^{
ASDN::MutexLocker l(__instanceLock__);
if (ASInterfaceStateIncludesDisplay(_interfaceState) == NO) {
ASDN::MutexLocker l(self->__instanceLock__);
if (ASInterfaceStateIncludesDisplay(self->_interfaceState) == NO) {
[self clearContents];
}
});
Expand Down
38 changes: 31 additions & 7 deletions Source/ASImageNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -737,19 +737,43 @@ asimagenode_modification_block_t ASImageNodeRoundBorderModificationBlock(CGFloat
{
return ^(UIImage *originalImage) {
ASGraphicsBeginImageContextWithOptions(originalImage.size, NO, originalImage.scale);
UIBezierPath *roundOutline = [UIBezierPath bezierPathWithOvalInRect:(CGRect){CGPointZero, originalImage.size}];

CGContextRef context = UIGraphicsGetCurrentContext();

CGRect rect = (CGRect){CGPointZero, originalImage.size};
CGMutablePathRef path = CGPathCreateMutable();

CGPathAddEllipseInRect(path, NULL, rect);
CGContextAddPath(context, path);

// Make the image round
[roundOutline addClip];
CGContextClip(context);

// Although drawAtPoint:blendMode: would consider the CTM already, we are using CGContext* functions for drawing
// the image instead calling drawAtPoint:blendMode. This will save use 50% of retain calls for the image
CGContextSetBlendMode(context, kCGBlendModeCopy);
CGContextTranslateCTM(context, 0, CGRectGetMaxY(rect) + CGRectGetMinY(rect));
CGContextScaleCTM(context, originalImage.scale, -originalImage.scale);
CGContextSetAlpha(context, 1.0);
CGContextDrawImage(context, rect, originalImage.CGImage);

// Draw the original image
[originalImage drawAtPoint:CGPointZero blendMode:kCGBlendModeCopy alpha:1];
CGPathRelease(path);

// Draw a border on top.
if (borderWidth > 0.0) {
[borderColor setStroke];
[roundOutline setLineWidth:borderWidth];
[roundOutline stroke];
// Begin a new path for the border
CGContextBeginPath(context);

CGFloat strokeThickness = borderWidth;
CGFloat strokeInset = floor((strokeThickness + 1.0f) / 2.0f) - 1.0f;
CGPathRef path = CGPathCreateWithEllipseInRect(CGRectInset(rect, strokeInset, strokeInset), NULL);
CGContextAddPath(context, path);

CGContextSetStrokeColorWithColor(context, borderColor.CGColor);
CGContextSetLineWidth(context, borderWidth);
CGContextStrokePath(context);

CGPathRelease(path);
}

return ASGraphicsGetImageAndEndCurrentContext();
Expand Down
2 changes: 1 addition & 1 deletion Source/Base/ASBaseDefines.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@
#endif
#endif

#define ASOVERLOADABLE __attribute__((overloadable))
#define AS_OVERLOADABLE __attribute__((overloadable))


#if __has_attribute(noescape)
Expand Down
9 changes: 8 additions & 1 deletion Source/Details/CoreGraphics+ASConvenience.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@

#import <Foundation/Foundation.h>

#import <CoreGraphics/CoreGraphics.h>
#import <tgmath.h>
#import <UIKit/UIBezierPath.h>

#import <AsyncDisplayKit/ASBaseDefines.h>

Expand Down Expand Up @@ -56,4 +56,11 @@ ASDISPLAYNODE_INLINE BOOL CGSizeEqualToSizeWithIn(CGSize size1, CGSize size2, CG
return fabs(size1.width - size2.width) < delta && fabs(size1.height - size2.height) < delta;
};

AS_OVERLOADABLE AS_WARN_UNUSED_RESULT AS_EXTERN CGPathRef ASCGRoundedPathCreate(CGRect rect, UIRectCorner corners, CGSize cornerRadii);

AS_OVERLOADABLE ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT CGPathRef ASCGRoundedPathCreate(CGRect rect, CGFloat cornerRadius) {
return ASCGRoundedPathCreate(rect, UIRectCornerAllCorners, CGSizeMake(cornerRadius, cornerRadius));
}


NS_ASSUME_NONNULL_END
19 changes: 0 additions & 19 deletions Source/Details/CoreGraphics+ASConvenience.m

This file was deleted.

Loading

0 comments on commit eb4c21c

Please sign in to comment.