From dbe2f321bc37fc308c3b75ef8c48d77b7a4b17be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9A=90=E9=A3=8E?= Date: Wed, 5 Apr 2017 17:50:47 +0800 Subject: [PATCH 01/10] * [ios] remove NSTextStorage lock because it is not necessary for thread safety --- .../Sources/Component/WXTextComponent.m | 46 +------------------ 1 file changed, 1 insertion(+), 45 deletions(-) diff --git a/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.m b/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.m index f18ac94589..77e7ce2794 100644 --- a/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.m +++ b/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.m @@ -119,16 +119,6 @@ @implementation WXTextComponent WXTextDecoration _textDecoration; NSString *_textOverflow; CGFloat _lineHeight; - - - pthread_mutex_t _textStorageMutex; - pthread_mutexattr_t _textStorageMutexAttr; -} - -static BOOL _isUsingTextStorageLock = NO; -+ (void)useTextStorageLock:(BOOL)isUsingTextStorageLock -{ - _isUsingTextStorageLock = isUsingTextStorageLock; } - (instancetype)initWithRef:(NSString *)ref @@ -140,12 +130,6 @@ - (instancetype)initWithRef:(NSString *)ref { self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance]; if (self) { - if (_isUsingTextStorageLock) { - pthread_mutexattr_init(&_textStorageMutexAttr); - pthread_mutexattr_settype(&_textStorageMutexAttr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&_textStorageMutex, &_textStorageMutexAttr); - } - [self fillCSSStyles:styles]; [self fillAttributes:attributes]; } @@ -155,10 +139,6 @@ - (instancetype)initWithRef:(NSString *)ref - (void)dealloc { - if (_isUsingTextStorageLock) { - pthread_mutex_destroy(&_textStorageMutex); - pthread_mutexattr_destroy(&_textStorageMutexAttr); - } [[NSNotificationCenter defaultCenter] removeObserver:self]; } @@ -226,13 +206,7 @@ - (void)fillAttributes:(NSDictionary *)attributes - (void)setNeedsRepaint { - if (_isUsingTextStorageLock) { - pthread_mutex_lock(&_textStorageMutex); - } _textStorage = nil; - if (_isUsingTextStorageLock) { - pthread_mutex_unlock(&_textStorageMutex); - } } #pragma mark - Subclass @@ -244,13 +218,7 @@ - (void)setNeedsLayout - (void)viewDidLoad { - if (_isUsingTextStorageLock) { - pthread_mutex_lock(&_textStorageMutex); - } ((WXText *)self.view).textStorage = _textStorage; - if (_isUsingTextStorageLock) { - pthread_mutex_unlock(&_textStorageMutex); - } [self setNeedsDisplay]; } @@ -435,14 +403,7 @@ - (NSTextStorage *)textStorageWithWidth:(CGFloat)width [layoutManager ensureLayoutForTextContainer:textContainer]; _textStorageWidth = width; - - if (_isUsingTextStorageLock) { - pthread_mutex_lock(&_textStorageMutex); - } _textStorage = textStorage; - if (_isUsingTextStorageLock) { - pthread_mutex_unlock(&_textStorageMutex); - } return textStorage; } @@ -454,13 +415,8 @@ - (void)syncTextStorageForView [self.weexInstance.componentManager _addUITask:^{ if ([self isViewLoaded]) { - if (_isUsingTextStorageLock) { - pthread_mutex_lock(&_textStorageMutex); - } ((WXText *)self.view).textStorage = textStorage; - if (_isUsingTextStorageLock) { - pthread_mutex_unlock(&_textStorageMutex); - } + [self readyToRender]; // notify super component [self setNeedsDisplay]; } From c6d980d30001fa50c25b6c91434cbbb85b24021f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9A=90=E9=A3=8E?= Date: Wed, 5 Apr 2017 19:48:08 +0800 Subject: [PATCH 02/10] * [ios] support view compositing --- .../Sources/Component/WXComponent_internal.h | 8 +- .../Sources/Component/WXImageComponent.m | 50 ++--- .../Sources/Component/WXTextComponent.m | 89 ++++---- .../Sources/Display/WXComponent+Display.m | 191 +++++++++++------- .../WeexSDK/Sources/Display/WXRoundedRect.h | 2 + .../WeexSDK/Sources/Display/WXRoundedRect.mm | 5 + ios/sdk/WeexSDK/Sources/Model/WXComponent.h | 55 ++++- ios/sdk/WeexSDK/Sources/Model/WXComponent.m | 7 +- ios/sdk/WeexSDK/Sources/Model/WXSDKInstance.m | 6 +- 9 files changed, 248 insertions(+), 165 deletions(-) diff --git a/ios/sdk/WeexSDK/Sources/Component/WXComponent_internal.h b/ios/sdk/WeexSDK/Sources/Component/WXComponent_internal.h index 28f9d2ee04..00f0ef4349 100644 --- a/ios/sdk/WeexSDK/Sources/Component/WXComponent_internal.h +++ b/ios/sdk/WeexSDK/Sources/Component/WXComponent_internal.h @@ -81,8 +81,8 @@ * Display */ CALayer *_layer; - BOOL _composite; - BOOL _compositingChild; + BOOL _useCompositing; + BOOL _isCompositingChild; WXThreadSafeCounter *_displayCounter; UIColor *_borderTopColor; @@ -185,6 +185,10 @@ - (void)_setupNavBarWithStyles:(NSMutableDictionary *)styles attributes:(NSMutableDictionary *)attributes; +- (void)_initCompositingAttribute:(NSDictionary *)attributes; + +- (BOOL)_bitmapOpaqueWithSize:(CGSize)size; + - (void)_updateNavBarAttributes:(NSDictionary *)attributes; - (void)_handleFirstScreenTime; diff --git a/ios/sdk/WeexSDK/Sources/Component/WXImageComponent.m b/ios/sdk/WeexSDK/Sources/Component/WXImageComponent.m index d5d9ca6a87..1e2594c477 100644 --- a/ios/sdk/WeexSDK/Sources/Component/WXImageComponent.m +++ b/ios/sdk/WeexSDK/Sources/Component/WXImageComponent.m @@ -158,38 +158,21 @@ - (void)viewDidLoad } -- (WXDisplayBlock)displayBlock +- (BOOL)needsDrawRect { - if ([self isViewLoaded]) { - // if has a image view, image is setted by image view, displayBlock is not needed - return nil; + if (_isCompositingChild) { + return YES; + } else { + return NO; } - - __weak typeof(self) weakSelf = self; - return ^UIImage *(CGRect bounds, BOOL(^isCancelled)(void)) { - if (isCancelled()) { - return nil; - } - - if (!weakSelf.image) { - [weakSelf updateImage]; - return nil; - } - - if (isCancelled && isCancelled()) { - return nil; - } - - UIGraphicsBeginImageContextWithOptions(bounds.size, self.layer.opaque, 1.0); - - [weakSelf.image drawInRect:bounds]; - - UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); - - UIGraphicsEndImageContext(); - - return image; - }; +} + +- (UIImage *)drawRect:(CGRect)rect +{ + if (!self.image) { + [self updateImage]; + } + return self.image; } - (void)viewWillUnload @@ -262,6 +245,9 @@ - (void)updatePlaceHolderWithFailedBlock:(void(^)(NSString *, NSError *))downloa ((UIImageView *)strongSelf.view).image = image; weakSelf.imageDownloadFinish = YES; [self readyToRender]; + } else if (strongSelf->_isCompositingChild) { + strongSelf->_image = image; + weakSelf.imageDownloadFinish = YES; } }); }]; @@ -302,6 +288,10 @@ - (void)updateContentImageWithFailedBlock:(void(^)(NSString *, NSError *))downlo strongSelf.imageDownloadFinish = YES; ((UIImageView *)strongSelf.view).image = image; [strongSelf readyToRender]; + } else if (strongSelf->_isCompositingChild) { + strongSelf.imageDownloadFinish = YES; + strongSelf->_image = image; + [strongSelf setNeedsDisplay]; } }); }]; diff --git a/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.m b/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.m index 77e7ce2794..4ee386045a 100644 --- a/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.m +++ b/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.m @@ -42,37 +42,6 @@ + (Class)layerClass return [WXLayer class]; } -- (UIImage *)drawTextWithBounds:(CGRect)bounds padding:(UIEdgeInsets)padding -{ - if (bounds.size.width <=0 || bounds.size.height <= 0) { - return nil; - } - UIGraphicsBeginImageContextWithOptions(bounds.size, self.layer.opaque, WXScreenScale()); - CGContextRef context = UIGraphicsGetCurrentContext(); - - if ([self.wx_component _needsDrawBorder]) { - [self.wx_component _drawBorderWithContext:context size:bounds.size]; - } else { - WXPerformBlockOnMainThread(^{ - [self.wx_component _resetNativeBorderRadius]; - }); - } - NSLayoutManager *layoutManager = _textStorage.layoutManagers.firstObject; - NSTextContainer *textContainer = layoutManager.textContainers.firstObject; - - CGRect textFrame = UIEdgeInsetsInsetRect(bounds, padding); - NSRange glyphRange = [layoutManager glyphRangeForTextContainer:textContainer]; - - [layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:textFrame.origin]; - [layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:textFrame.origin]; - - UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); - - UIGraphicsEndImageContext(); - - return image; -} - - (void)setTextStorage:(NSTextStorage *)textStorage { if (_textStorage != textStorage) { @@ -227,25 +196,21 @@ - (UIView *)loadView return [[WXText alloc] init]; } -- (WXDisplayBlock)displayBlock +- (BOOL)needsDrawRect { - WXText *textView = ((WXText *)self.view); - return ^UIImage *(CGRect bounds, BOOL(^isCancelled)(void)) { - if (isCancelled()) { - return nil; - } - if (_isUsingTextStorageLock) { - pthread_mutex_lock(&_textStorageMutex); - } - - UIImage *image = [textView drawTextWithBounds:bounds padding:_padding]; - - if (_isUsingTextStorageLock) { - pthread_mutex_unlock(&_textStorageMutex); - } - - return image; - }; + return YES; +} + +- (UIImage *)drawRect:(CGRect)rect +{ + if (_isCompositingChild) { + [self drawTextWithBounds:rect padding:_padding view:nil]; + } else { + WXText *textView = ((WXText *)self.view); + [self drawTextWithBounds:rect padding:_padding view:textView]; + } + + return nil; } - (CGSize (^)(CGSize))measureBlock @@ -287,6 +252,7 @@ - (WXDisplayBlock)displayBlock } #pragma mark Text Building + - (NSString *)text { return _text; @@ -447,6 +413,31 @@ - (void)_updateAttributesOnComponentThread:(NSDictionary *)attributes [self syncTextStorageForView]; } +- (void)drawTextWithBounds:(CGRect)bounds padding:(UIEdgeInsets)padding view:(WXText *)view +{ + if (bounds.size.width <=0 || bounds.size.height <= 0) { + return; + } + + CGContextRef context = UIGraphicsGetCurrentContext(); + + if ([self _needsDrawBorder]) { + [self _drawBorderWithContext:context size:bounds.size]; + } else { + WXPerformBlockOnMainThread(^{ + [self _resetNativeBorderRadius]; + }); + } + NSLayoutManager *layoutManager = (view ? view.textStorage : _textStorage).layoutManagers.firstObject; + NSTextContainer *textContainer = layoutManager.textContainers.firstObject; + + CGRect textFrame = UIEdgeInsetsInsetRect(bounds, padding); + NSRange glyphRange = [layoutManager glyphRangeForTextContainer:textContainer]; + + [layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:textFrame.origin]; + [layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:textFrame.origin]; +} + #ifdef UITEST - (NSString *)description { diff --git a/ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m b/ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m index 2231d468e1..fdd02a133c 100644 --- a/ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m +++ b/ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m @@ -28,10 +28,10 @@ - (void)setNeedsDisplay { WXAssertMainThread(); - if (_compositingChild) { + if (_isCompositingChild) { WXComponent *supercomponent = self.supercomponent; while (supercomponent) { - if (supercomponent->_composite) { + if (supercomponent->_useCompositing) { break; } supercomponent = supercomponent.supercomponent; @@ -44,54 +44,82 @@ - (void)setNeedsDisplay } } -- (WXDisplayBlock)displayBlock +- (BOOL)needsDrawRect +{ + if (![self _needsDrawBorder]) { + WXLogDebug(@"No need to draw border for %@", self.ref); + WXPerformBlockOnMainThread(^{ + [self _resetNativeBorderRadius]; + }); + + return NO; + } + + return YES; +} + +- (UIImage *)drawRect:(CGRect)rect +{ + CGSize size = rect.size; + if (size.width <= 0 || size.height <= 0) { + WXLogDebug(@"No need to draw border for %@, because width or height is zero", self.ref); + return nil; + } + + CGContextRef context = UIGraphicsGetCurrentContext(); + [self _drawBorderWithContext:context size:size]; + + return nil; +} + +- (WXDisplayBlock)_displayBlock { WXDisplayBlock displayBlock = ^UIImage *(CGRect bounds, BOOL(^isCancelled)(void)) { if (isCancelled()) { return nil; } - - if (![self _needsDrawBorder]) { - WXLogDebug(@"No need to draw border for %@", self.ref); - WXPerformBlockOnMainThread(^{ - [self _resetNativeBorderRadius]; - }); - return nil; + + UIGraphicsBeginImageContextWithOptions(bounds.size, [self _bitmapOpaqueWithSize:bounds.size] , 0.0); + UIImage *image = [self drawRect:bounds]; + if (!image) { + image = UIGraphicsGetImageFromCurrentImageContext(); } + UIGraphicsEndImageContext(); - return [self _borderImage]; + return image; }; return displayBlock; } -- (WXDisplayCompletionBlock)displayCompletionBlock +#pragma mark Private + +- (void)_initCompositingAttribute:(NSDictionary *)attributes { - return nil; + _useCompositing = attributes[@"compositing"] ? [WXConvert BOOL:attributes[@"compositing"]] : NO; } -#pragma mark Private - - (void)_willDisplayLayer:(CALayer *)layer { WXAssertMainThread(); - if (_compositingChild) { - // compsiting children need not draw layer + if (_isCompositingChild) { + // compsiting children do not have own layers, so return here. return; } CGRect displayBounds = CGRectMake(0, 0, self.calculatedFrame.size.width, self.calculatedFrame.size.height); + BOOL needsDrawRect = [self needsDrawRect]; WXDisplayBlock displayBlock; - if (_composite) { + if (_useCompositing) { displayBlock = [self _compositeDisplayBlock]; } else { - displayBlock = [self displayBlock]; + displayBlock = [self _displayBlock]; } WXDisplayCompletionBlock completionBlock = [self displayCompletionBlock]; - if (!displayBlock) { + if (!displayBlock || !needsDrawRect) { if (completionBlock) { completionBlock(layer, NO); } @@ -146,6 +174,22 @@ - (void)_willDisplayLayer:(CALayer *)layer } } +- (CGContextRef)beginDrawContext:(CGRect)bounds +{ + UIGraphicsBeginImageContextWithOptions(bounds.size, [self _bitmapOpaqueWithSize:bounds.size], 0.0); + CGContextRef context = UIGraphicsGetCurrentContext(); + + return context; +} + +- (UIImage *)endDrawContext +{ + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + return image; +} + - (WXDisplayBlock)_compositeDisplayBlock { return ^UIImage* (CGRect bounds, BOOL(^isCancelled)(void)) { @@ -153,11 +197,20 @@ - (WXDisplayBlock)_compositeDisplayBlock return nil; } NSMutableArray *displayBlocks = [NSMutableArray array]; - [self _collectDisplayBlocks:displayBlocks isCancelled:isCancelled]; - BOOL opaque = _layer.opaque && CGColorGetAlpha(_backgroundColor.CGColor) == 1.0f; + CGContextRef context = [self beginDrawContext:bounds]; + +// float scaleFactor = [[UIScreen mainScreen] scale]; +// CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); +// CGContextRef context = CGBitmapContextCreate(NULL, bounds.size.width * scaleFactor, bounds.size.height * scaleFactor, 8, 4 * bounds.size.width * scaleFactor, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); +// CGContextScaleCTM(context, scaleFactor, scaleFactor); +// +// // Adjusts position and invert the image. +// // The OpenGL uses the image data upside-down compared commom image files. +// CGContextTranslateCTM(context, 0, bounds.size.height); +// CGContextScaleCTM(context, 1.0, -1.0); - UIGraphicsBeginImageContextWithOptions(bounds.size, opaque, 0.0); + [self _collectCompositingDisplayBlocks:displayBlocks context:context isCancelled:isCancelled]; for (dispatch_block_t block in displayBlocks) { if (isCancelled()) { @@ -167,36 +220,32 @@ - (WXDisplayBlock)_compositeDisplayBlock block(); } - UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); +// CGImageRef imageRef= CGBitmapContextCreateImage(context); +// UIImage *image = [[UIImage alloc] initWithCGImage:imageRef]; +// CGColorSpaceRelease(colorSpace); +// CGContextRelease(context); + + UIImage *image = [self endDrawContext]; return image; }; } -- (void)_collectDisplayBlocks:(NSMutableArray *)displayBlocks isCancelled:(BOOL(^)(void))isCancelled +- (void)_collectCompositingDisplayBlocks:(NSMutableArray *)displayBlocks context:(CGContextRef)context isCancelled:(BOOL(^)(void))isCancelled { - // compositingChild has no chance to applyPropertiesToView, so force updateNode - // if (_compositingChild) { - // if (_data) { - // dispatch_async(dispatch_get_main_queue(), ^{ - // [self updateNode:_data]; - // }); - // } - // } - + // TODO: compositingChild has no chance to applyPropertiesToView, need update here? UIColor *backgroundColor = _backgroundColor; BOOL clipsToBounds = _clipToBounds; CGRect frame = self.calculatedFrame; CGRect bounds = CGRectMake(0, 0, frame.size.width, frame.size.height); - if (_composite) { + if (_useCompositing) { frame.origin = CGPointMake(0, 0); } - WXDisplayBlock displayBlock = [self displayBlock]; + BOOL needsDrawRect = [self needsDrawRect]; - BOOL shouldDisplay = displayBlock || backgroundColor || CGPointEqualToPoint(CGPointZero, frame.origin) == NO || clipsToBounds; + BOOL shouldDisplay = needsDrawRect && (backgroundColor || CGPointEqualToPoint(CGPointZero, frame.origin) == NO || clipsToBounds); if (shouldDisplay) { dispatch_block_t displayBlockToPush = ^{ @@ -204,21 +253,17 @@ - (void)_collectDisplayBlocks:(NSMutableArray *)displayBlocks isCancelled:(BOOL( CGContextSaveGState(context); CGContextTranslateCTM(context, frame.origin.x, frame.origin.y); - if (_compositingChild && clipsToBounds) { - [[UIBezierPath bezierPathWithRect:bounds] addClip]; + if (isCancelled && isCancelled()) { + return ; } - CGColorRef backgroundCGColor = backgroundColor.CGColor; - if (backgroundColor && CGColorGetAlpha(backgroundCGColor) > 0.0) { - CGContextSetFillColorWithColor(context, backgroundCGColor); - CGContextFillRect(context, bounds); + if (_isCompositingChild && clipsToBounds) { + [[UIBezierPath bezierPathWithRect:bounds] addClip]; } - if (displayBlock) { - UIImage *image = displayBlock(bounds, isCancelled); - if (image) { - [image drawInRect:bounds]; - } + UIImage *image = [self drawRect:bounds]; + if (image) { + [image drawInRect:bounds]; } }; [displayBlocks addObject:[displayBlockToPush copy]]; @@ -226,13 +271,12 @@ - (void)_collectDisplayBlocks:(NSMutableArray *)displayBlocks isCancelled:(BOOL( for (WXComponent *component in self.subcomponents) { if (!isCancelled()) { - [component _collectDisplayBlocks:displayBlocks isCancelled:isCancelled]; + [component _collectCompositingDisplayBlocks:displayBlocks context:context isCancelled:isCancelled]; } } if (shouldDisplay) { dispatch_block_t blockToPop = ^{ - CGContextRef context = UIGraphicsGetCurrentContext(); CGContextRestoreGState(context); }; [displayBlocks addObject:[blockToPop copy]]; @@ -241,26 +285,6 @@ - (void)_collectDisplayBlocks:(NSMutableArray *)displayBlocks isCancelled:(BOOL( #pragma mark Border Drawing -- (UIImage *)_borderImage -{ - CGSize size = self.calculatedFrame.size; - if (size.width <= 0 || size.height <= 0) { - WXLogDebug(@"No need to draw border for %@, because width or height is zero", self.ref); - return nil; - } - - WXLogDebug(@"Begin to draw border for %@", self.ref); - UIGraphicsBeginImageContextWithOptions(size, NO, 0.0); - CGContextRef context = UIGraphicsGetCurrentContext(); - - [self _drawBorderWithContext:context size:size]; - - UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - - return image; -} - - (void)_drawBorderWithContext:(CGContextRef)context size:(CGSize)size { CGRect rect = CGRectMake(0, 0, size.width, size.height); @@ -353,6 +377,10 @@ - (void)_drawBorderWithContext:(CGContextRef)context size:(CGSize)size - (BOOL)_needsDrawBorder { + if (_isCompositingChild) { + return YES; + } + if (![_layer isKindOfClass:[WXLayer class]]) { // Only support WXLayer return NO; @@ -491,4 +519,25 @@ - (void)_handleBorders:(NSDictionary *)styles isUpdating:(BOOL)updating } } +- (BOOL)_bitmapOpaqueWithSize:(CGSize)size +{ + CGRect rect = CGRectMake(0, 0, size.width, size.height); + WXRoundedRect *borderRect = [[WXRoundedRect alloc] initWithRect:rect topLeft:_borderTopLeftRadius topRight:_borderTopRightRadius bottomLeft:_borderBottomLeftRadius bottomRight:_borderBottomRightRadius]; + WXRadii *radii = borderRect.radii; + BOOL hasBorderRadius = [radii hasBorderRadius]; + return (!hasBorderRadius) && CGColorGetAlpha(_backgroundColor.CGColor) == 1.0; +} + +#pragma mark - Deprecated + +- (WXDisplayBlock)displayBlock +{ + return [self _displayBlock]; +} + +- (WXDisplayCompletionBlock)displayCompletionBlock +{ + return nil; +} + @end diff --git a/ios/sdk/WeexSDK/Sources/Display/WXRoundedRect.h b/ios/sdk/WeexSDK/Sources/Display/WXRoundedRect.h index 9cabf8e81e..4cde88ae33 100644 --- a/ios/sdk/WeexSDK/Sources/Display/WXRoundedRect.h +++ b/ios/sdk/WeexSDK/Sources/Display/WXRoundedRect.h @@ -17,6 +17,8 @@ @property (nonatomic, assign) CGFloat bottomLeft; @property (nonatomic, assign) CGFloat bottomRight; +- (BOOL)hasBorderRadius; + @end @interface WXRoundedRect : NSObject diff --git a/ios/sdk/WeexSDK/Sources/Display/WXRoundedRect.mm b/ios/sdk/WeexSDK/Sources/Display/WXRoundedRect.mm index ae535dd65b..fb624656db 100644 --- a/ios/sdk/WeexSDK/Sources/Display/WXRoundedRect.mm +++ b/ios/sdk/WeexSDK/Sources/Display/WXRoundedRect.mm @@ -26,6 +26,11 @@ - (instancetype)initWithTopLeft:(CGFloat)topLeft return self; } +- (BOOL)hasBorderRadius +{ + return _topLeft > 0.001 || _topRight > 0.001 || _bottomLeft > 0.001 || _bottomRight > 0.001; +} + - (void)scale:(float)factor { if (factor == 1) { diff --git a/ios/sdk/WeexSDK/Sources/Model/WXComponent.h b/ios/sdk/WeexSDK/Sources/Model/WXComponent.h index a2f75d7c7a..6bd7cd3138 100644 --- a/ios/sdk/WeexSDK/Sources/Model/WXComponent.h +++ b/ios/sdk/WeexSDK/Sources/Model/WXComponent.h @@ -324,34 +324,69 @@ NS_ASSUME_NONNULL_BEGIN /// @name Display ///-------------------------------------- -typedef UIImage * _Nonnull(^WXDisplayBlock)(CGRect bounds, BOOL(^isCancelled)(void)); -typedef void(^WXDisplayCompletionBlock)(CALayer *layer, BOOL finished); - /** * @abstract Marks the view as needing display. The method should be called on the main thread. + * @discussion You can use this method to notify the system that your component's contents need to be redrawn. This method makes a note of the request and returns immediately. The component is not actually redrawn until the next drawing cycle, at which point all invalidated components are updated. + * */ - (void)setNeedsDisplay; +- (CGContextRef)beginDrawContext:(CGRect)bounds; + +- (UIImage *)endDrawContext; + /** - * @abstract Return a block to be called to draw layer. - * - * @discussion The block returned will be called on any thread. - * + * @abstract Returns a Boolean indicating whether the component needs to be drawn by `drawRect:` + */ +- (BOOL)needsDrawRect; + +/** + * @abstract Draws the component’s image within the passed-in rectangle. + * @parameter rect The rectangle which is the entire visible bounds of your component. + * @return A UIImage containing the contents of the current bitmap graphics context. + * @discussion + * Subclasses that use technologies such as Core Graphics and UIKit to draw their own component’s content should override this method and implement their drawing code there. You do not need to override this method if your component sets its content in superclass's way. + * By the time this method is called, UIKit has configured the drawing environment appropriately for your view and you can simply call whatever drawing methods and functions you need to render your content. Specifically, Weex creates and configures a graphics context for drawing and adjusts the transform of that context so that its origin matches the origin of your components’s bounds rectangle. You can get a reference to the graphics context using the `UIGraphicsGetCurrentContext` function, but do not establish a strong reference to the graphics context because it can change between calls to the drawRect: method. + * If you already have an image that represents the content of the component, then you should just return the image and do no drawing, otherwise you should draw your content in the current context and return nil. + * You should never call this method directly yourself. To invalidate part of your component's content, and thus cause that portion to be redrawn, call the `setNeedsDisplay` method instead. + */ +- (UIImage *)drawRect:(CGRect)rect; + +/** + * @abstract Called when a component finishes rendering its content. + * @discussion Do not call this method directly. Weex calls this method at appropriate times to finish updating the component's content. + * Subclasses can override this method to perform additional work on components that were rendered. */ -- (WXDisplayBlock)displayBlock; +- (void)displayDidFinished:(BOOL)success; /** - * readyToRender + * readyToRender, do not use it, will be deprecated soon */ - (void)readyToRender; +@end + +@interface WXComponent (Deprecated) + +typedef UIImage * _Nonnull(^WXDisplayBlock)(CGRect bounds, BOOL(^isCancelled)(void)); +typedef void(^WXDisplayCompletionBlock)(CALayer *layer, BOOL finished); + +/** + * @abstract Return a block to be called to draw layer. + * + * @discussion The block returned will be called on any thread. + * + */ +- (WXDisplayBlock)displayBlock DEPRECATED_MSG_ATTRIBUTE("use drawRect method instead."); + /** * @abstract Return a block to be called while drawing is finished. * * @discussion The block returned will be called on main thread. * */ -- (WXDisplayCompletionBlock)displayCompletionBlock; +- (WXDisplayCompletionBlock)displayCompletionBlock DEPRECATED_MSG_ATTRIBUTE("use displayDidFinished method instead."); + @end diff --git a/ios/sdk/WeexSDK/Sources/Model/WXComponent.m b/ios/sdk/WeexSDK/Sources/Model/WXComponent.m index cecc708935..08a1bccf36 100644 --- a/ios/sdk/WeexSDK/Sources/Model/WXComponent.m +++ b/ios/sdk/WeexSDK/Sources/Model/WXComponent.m @@ -95,6 +95,7 @@ - (instancetype)initWithRef:(NSString *)ref [self _setupNavBarWithStyles:_styles attributes:_attributes]; [self _initCSSNodeWithStyles:_styles]; [self _initViewPropertyWithStyles:_styles]; + [self _initCompositingAttribute:_attributes]; [self _handleBorders:styles isUpdating:NO]; } @@ -180,7 +181,7 @@ - (UIView *)view WXAssertMainThread(); // compositing child will be drew by its composited ancestor - if (_compositingChild) { + if (_isCompositingChild) { return nil; } @@ -332,6 +333,10 @@ - (void)_insertSubcomponent:(WXComponent *)subcomponent atIndex:(NSInteger)index subcomponent->_isNeedJoinLayoutSystem = NO; } + if (_useCompositing || _isCompositingChild) { + subcomponent->_isCompositingChild = YES; + } + [self _recomputeCSSNodeChildren]; [self setNeedsLayout]; } diff --git a/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance.m b/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance.m index 83bcd7d1cc..66985a97ad 100644 --- a/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance.m +++ b/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance.m @@ -292,8 +292,10 @@ - (void)destroyInstance } [[WXSDKManager bridgeMgr] destroyInstance:self.instanceId]; - - [self.componentManager invalidate]; + + if (_componentManager) { + [_componentManager invalidate]; + } __weak typeof(self) weakSelf = self; WXPerformBlockOnComponentThread(^{ __strong typeof(self) strongSelf = weakSelf; From 3a714ac333b2d230d11e6c8c2e94ab94988049da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9A=90=E9=A3=8E?= Date: Wed, 5 Apr 2017 20:17:20 +0800 Subject: [PATCH 03/10] * [ios] use bitmap context to draw compositing --- .../Sources/Component/WXImageComponent.m | 2 +- .../Sources/Component/WXTextComponent.m | 10 ++-- .../Sources/Display/WXComponent+Display.m | 49 ++++++++++++------- ios/sdk/WeexSDK/Sources/Model/WXComponent.h | 4 +- 4 files changed, 38 insertions(+), 27 deletions(-) diff --git a/ios/sdk/WeexSDK/Sources/Component/WXImageComponent.m b/ios/sdk/WeexSDK/Sources/Component/WXImageComponent.m index 1e2594c477..2527adcdc7 100644 --- a/ios/sdk/WeexSDK/Sources/Component/WXImageComponent.m +++ b/ios/sdk/WeexSDK/Sources/Component/WXImageComponent.m @@ -167,7 +167,7 @@ - (BOOL)needsDrawRect } } -- (UIImage *)drawRect:(CGRect)rect +- (UIImage *)drawRect:(CGRect)rect withContext:(CGContextRef)context; { if (!self.image) { [self updateImage]; diff --git a/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.m b/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.m index 4ee386045a..ff98d2d99e 100644 --- a/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.m +++ b/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.m @@ -201,13 +201,13 @@ - (BOOL)needsDrawRect return YES; } -- (UIImage *)drawRect:(CGRect)rect +- (UIImage *)drawRect:(CGRect)rect withContext:(CGContextRef)context; { if (_isCompositingChild) { - [self drawTextWithBounds:rect padding:_padding view:nil]; + [self drawTextWithContext:context bounds:rect padding:_padding view:nil]; } else { WXText *textView = ((WXText *)self.view); - [self drawTextWithBounds:rect padding:_padding view:textView]; + [self drawTextWithContext:context bounds:rect padding:_padding view:textView]; } return nil; @@ -413,14 +413,12 @@ - (void)_updateAttributesOnComponentThread:(NSDictionary *)attributes [self syncTextStorageForView]; } -- (void)drawTextWithBounds:(CGRect)bounds padding:(UIEdgeInsets)padding view:(WXText *)view +- (void)drawTextWithContext:(CGContextRef)context bounds:(CGRect)bounds padding:(UIEdgeInsets)padding view:(WXText *)view { if (bounds.size.width <=0 || bounds.size.height <= 0) { return; } - CGContextRef context = UIGraphicsGetCurrentContext(); - if ([self _needsDrawBorder]) { [self _drawBorderWithContext:context size:bounds.size]; } else { diff --git a/ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m b/ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m index fdd02a133c..b548663b15 100644 --- a/ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m +++ b/ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m @@ -58,7 +58,7 @@ - (BOOL)needsDrawRect return YES; } -- (UIImage *)drawRect:(CGRect)rect +- (UIImage *)drawRect:(CGRect)rect withContext:(CGContextRef)context { CGSize size = rect.size; if (size.width <= 0 || size.height <= 0) { @@ -66,7 +66,6 @@ - (UIImage *)drawRect:(CGRect)rect return nil; } - CGContextRef context = UIGraphicsGetCurrentContext(); [self _drawBorderWithContext:context size:size]; return nil; @@ -80,7 +79,8 @@ - (WXDisplayBlock)_displayBlock } UIGraphicsBeginImageContextWithOptions(bounds.size, [self _bitmapOpaqueWithSize:bounds.size] , 0.0); - UIImage *image = [self drawRect:bounds]; + CGContextRef context = UIGraphicsGetCurrentContext(); + UIImage *image = [self drawRect:bounds withContext:context]; if (!image) { image = UIGraphicsGetImageFromCurrentImageContext(); } @@ -176,16 +176,32 @@ - (void)_willDisplayLayer:(CALayer *)layer - (CGContextRef)beginDrawContext:(CGRect)bounds { - UIGraphicsBeginImageContextWithOptions(bounds.size, [self _bitmapOpaqueWithSize:bounds.size], 0.0); - CGContextRef context = UIGraphicsGetCurrentContext(); +// UIGraphicsBeginImageContextWithOptions(bounds.size, [self _bitmapOpaqueWithSize:bounds.size], 0.0); +// CGContextRef context = UIGraphicsGetCurrentContext(); + + float scaleFactor = [[UIScreen mainScreen] scale]; + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGContextRef context = CGBitmapContextCreate(NULL, bounds.size.width * scaleFactor, bounds.size.height * scaleFactor, 8, 4 * bounds.size.width * scaleFactor, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); + CGContextScaleCTM(context, scaleFactor, scaleFactor); + + // Adjusts position and invert the image. + // The OpenGL uses the image data upside-down compared commom image files. + CGContextTranslateCTM(context, 0, bounds.size.height); + CGContextScaleCTM(context, 1.0, -1.0); + + CGColorSpaceRelease(colorSpace); return context; } -- (UIImage *)endDrawContext +- (UIImage *)endDrawContext:(CGContextRef)context { - UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); +// UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); +// UIGraphicsEndImageContext(); + + CGImageRef imageRef= CGBitmapContextCreateImage(context); + UIImage *image = [[UIImage alloc] initWithCGImage:imageRef]; + CGContextRelease(context); return image; } @@ -220,13 +236,7 @@ - (WXDisplayBlock)_compositeDisplayBlock block(); } -// CGImageRef imageRef= CGBitmapContextCreateImage(context); -// UIImage *image = [[UIImage alloc] initWithCGImage:imageRef]; - -// CGColorSpaceRelease(colorSpace); -// CGContextRelease(context); - - UIImage *image = [self endDrawContext]; + UIImage *image = [self endDrawContext:context]; return image; }; } @@ -249,7 +259,9 @@ - (void)_collectCompositingDisplayBlocks:(NSMutableArray *)displayBlocks context if (shouldDisplay) { dispatch_block_t displayBlockToPush = ^{ - CGContextRef context = UIGraphicsGetCurrentContext(); + if (!context) { + + } CGContextSaveGState(context); CGContextTranslateCTM(context, frame.origin.x, frame.origin.y); @@ -261,9 +273,10 @@ - (void)_collectCompositingDisplayBlocks:(NSMutableArray *)displayBlocks context [[UIBezierPath bezierPathWithRect:bounds] addClip]; } - UIImage *image = [self drawRect:bounds]; + UIImage *image = [self drawRect:bounds withContext:context]; if (image) { - [image drawInRect:bounds]; + CGContextDrawImage(context, bounds, image.CGImage); +// [image drawInRect:bounds]; } }; [displayBlocks addObject:[displayBlockToPush copy]]; diff --git a/ios/sdk/WeexSDK/Sources/Model/WXComponent.h b/ios/sdk/WeexSDK/Sources/Model/WXComponent.h index 6bd7cd3138..d9e7070c32 100644 --- a/ios/sdk/WeexSDK/Sources/Model/WXComponent.h +++ b/ios/sdk/WeexSDK/Sources/Model/WXComponent.h @@ -333,7 +333,7 @@ NS_ASSUME_NONNULL_BEGIN - (CGContextRef)beginDrawContext:(CGRect)bounds; -- (UIImage *)endDrawContext; +- (UIImage *)endDrawContext:(CGContextRef)context; /** * @abstract Returns a Boolean indicating whether the component needs to be drawn by `drawRect:` @@ -350,7 +350,7 @@ NS_ASSUME_NONNULL_BEGIN * If you already have an image that represents the content of the component, then you should just return the image and do no drawing, otherwise you should draw your content in the current context and return nil. * You should never call this method directly yourself. To invalidate part of your component's content, and thus cause that portion to be redrawn, call the `setNeedsDisplay` method instead. */ -- (UIImage *)drawRect:(CGRect)rect; +- (UIImage *)drawRect:(CGRect)rect withContext:(CGContextRef)context; /** * @abstract Called when a component finishes rendering its content. From d81b656f146b5642faf7aa38b1d469b50c6e8051 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9A=90=E9=A3=8E?= Date: Wed, 5 Apr 2017 20:25:42 +0800 Subject: [PATCH 04/10] * [ios] UIGraphicsPushContext to push context, fix invalid context 0x0 problem. --- .../WeexSDK/Sources/Component/WXImageComponent.m | 2 +- .../WeexSDK/Sources/Component/WXTextComponent.m | 3 ++- .../WeexSDK/Sources/Display/WXComponent+Display.m | 15 +++++++++------ ios/sdk/WeexSDK/Sources/Model/WXComponent.h | 2 +- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/ios/sdk/WeexSDK/Sources/Component/WXImageComponent.m b/ios/sdk/WeexSDK/Sources/Component/WXImageComponent.m index 2527adcdc7..73696fda03 100644 --- a/ios/sdk/WeexSDK/Sources/Component/WXImageComponent.m +++ b/ios/sdk/WeexSDK/Sources/Component/WXImageComponent.m @@ -167,7 +167,7 @@ - (BOOL)needsDrawRect } } -- (UIImage *)drawRect:(CGRect)rect withContext:(CGContextRef)context; +- (UIImage *)drawRect:(CGRect)rect; { if (!self.image) { [self updateImage]; diff --git a/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.m b/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.m index ff98d2d99e..42fccfb2c9 100644 --- a/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.m +++ b/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.m @@ -201,8 +201,9 @@ - (BOOL)needsDrawRect return YES; } -- (UIImage *)drawRect:(CGRect)rect withContext:(CGContextRef)context; +- (UIImage *)drawRect:(CGRect)rect; { + CGContextRef context = UIGraphicsGetCurrentContext(); if (_isCompositingChild) { [self drawTextWithContext:context bounds:rect padding:_padding view:nil]; } else { diff --git a/ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m b/ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m index b548663b15..6428ecb81f 100644 --- a/ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m +++ b/ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m @@ -58,7 +58,7 @@ - (BOOL)needsDrawRect return YES; } -- (UIImage *)drawRect:(CGRect)rect withContext:(CGContextRef)context +- (UIImage *)drawRect:(CGRect)rect { CGSize size = rect.size; if (size.width <= 0 || size.height <= 0) { @@ -66,6 +66,7 @@ - (UIImage *)drawRect:(CGRect)rect withContext:(CGContextRef)context return nil; } + CGContextRef context = UIGraphicsGetCurrentContext(); [self _drawBorderWithContext:context size:size]; return nil; @@ -79,8 +80,7 @@ - (WXDisplayBlock)_displayBlock } UIGraphicsBeginImageContextWithOptions(bounds.size, [self _bitmapOpaqueWithSize:bounds.size] , 0.0); - CGContextRef context = UIGraphicsGetCurrentContext(); - UIImage *image = [self drawRect:bounds withContext:context]; + UIImage *image = [self drawRect:bounds]; if (!image) { image = UIGraphicsGetImageFromCurrentImageContext(); } @@ -228,6 +228,8 @@ - (WXDisplayBlock)_compositeDisplayBlock [self _collectCompositingDisplayBlocks:displayBlocks context:context isCancelled:isCancelled]; + UIGraphicsPushContext(context); + for (dispatch_block_t block in displayBlocks) { if (isCancelled()) { UIGraphicsEndImageContext(); @@ -236,6 +238,8 @@ - (WXDisplayBlock)_compositeDisplayBlock block(); } + UIGraphicsPopContext(); + UIImage *image = [self endDrawContext:context]; return image; }; @@ -273,10 +277,9 @@ - (void)_collectCompositingDisplayBlocks:(NSMutableArray *)displayBlocks context [[UIBezierPath bezierPathWithRect:bounds] addClip]; } - UIImage *image = [self drawRect:bounds withContext:context]; + UIImage *image = [self drawRect:bounds]; if (image) { - CGContextDrawImage(context, bounds, image.CGImage); -// [image drawInRect:bounds]; + [image drawInRect:bounds]; } }; [displayBlocks addObject:[displayBlockToPush copy]]; diff --git a/ios/sdk/WeexSDK/Sources/Model/WXComponent.h b/ios/sdk/WeexSDK/Sources/Model/WXComponent.h index d9e7070c32..7da7bc6a3f 100644 --- a/ios/sdk/WeexSDK/Sources/Model/WXComponent.h +++ b/ios/sdk/WeexSDK/Sources/Model/WXComponent.h @@ -350,7 +350,7 @@ NS_ASSUME_NONNULL_BEGIN * If you already have an image that represents the content of the component, then you should just return the image and do no drawing, otherwise you should draw your content in the current context and return nil. * You should never call this method directly yourself. To invalidate part of your component's content, and thus cause that portion to be redrawn, call the `setNeedsDisplay` method instead. */ -- (UIImage *)drawRect:(CGRect)rect withContext:(CGContextRef)context; +- (UIImage *)drawRect:(CGRect)rect; /** * @abstract Called when a component finishes rendering its content. From 3844a2532e1ec046abae2fe6132ff1bbf0a432ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9A=90=E9=A3=8E?= Date: Thu, 6 Apr 2017 12:14:27 +0800 Subject: [PATCH 05/10] * [ios] deprecate displayBlock and completionDisplayBlock method --- .../Sources/Component/WXCellComponent.m | 10 +-- .../Sources/Display/WXComponent+Display.m | 73 ++++++++++--------- ios/sdk/WeexSDK/Sources/Model/WXComponent.h | 24 ++++-- 3 files changed, 58 insertions(+), 49 deletions(-) diff --git a/ios/sdk/WeexSDK/Sources/Component/WXCellComponent.m b/ios/sdk/WeexSDK/Sources/Component/WXCellComponent.m index 24a5a1d120..5e67ec7b6e 100644 --- a/ios/sdk/WeexSDK/Sources/Component/WXCellComponent.m +++ b/ios/sdk/WeexSDK/Sources/Component/WXCellComponent.m @@ -59,15 +59,9 @@ - (void)_frameDidCalculated:(BOOL)isChanged } } -- (WXDisplayCompletionBlock)displayCompletionBlock +- (void)didFinishDrawingLayer:(BOOL)success { - return ^(CALayer *layer, BOOL finished) { - if ([super displayCompletionBlock]) { - [super displayCompletionBlock](layer, finished); - } - - [self.delegate cellDidRendered:self]; - }; + [self.delegate cellDidRendered:self]; } - (void)updateAttributes:(NSDictionary *)attributes diff --git a/ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m b/ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m index 6428ecb81f..0d4bca1023 100644 --- a/ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m +++ b/ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m @@ -46,6 +46,10 @@ - (void)setNeedsDisplay - (BOOL)needsDrawRect { + if (_useCompositing || _isCompositingChild) { + return YES; + } + if (![self _needsDrawBorder]) { WXLogDebug(@"No need to draw border for %@", self.ref); WXPerformBlockOnMainThread(^{ @@ -72,13 +76,20 @@ - (UIImage *)drawRect:(CGRect)rect return nil; } +- (void)didFinishDrawingLayer:(BOOL)success +{ + WXAssertMainThread(); +} + +#pragma mark Private + - (WXDisplayBlock)_displayBlock { WXDisplayBlock displayBlock = ^UIImage *(CGRect bounds, BOOL(^isCancelled)(void)) { if (isCancelled()) { return nil; } - + UIGraphicsBeginImageContextWithOptions(bounds.size, [self _bitmapOpaqueWithSize:bounds.size] , 0.0); UIImage *image = [self drawRect:bounds]; if (!image) { @@ -92,7 +103,13 @@ - (WXDisplayBlock)_displayBlock return displayBlock; } -#pragma mark Private +- (WXDisplayCompletionBlock)_displayCompletionBlock +{ + __weak typeof(self) weakSelf = self; + return ^(CALayer *layer, BOOL finished) { + [weakSelf didFinishDrawingLayer:finished]; + }; +} - (void)_initCompositingAttribute:(NSDictionary *)attributes { @@ -117,7 +134,7 @@ - (void)_willDisplayLayer:(CALayer *)layer } else { displayBlock = [self _displayBlock]; } - WXDisplayCompletionBlock completionBlock = [self displayCompletionBlock]; + WXDisplayCompletionBlock completionBlock = [self _displayCompletionBlock]; if (!displayBlock || !needsDrawRect) { if (completionBlock) { @@ -176,32 +193,32 @@ - (void)_willDisplayLayer:(CALayer *)layer - (CGContextRef)beginDrawContext:(CGRect)bounds { -// UIGraphicsBeginImageContextWithOptions(bounds.size, [self _bitmapOpaqueWithSize:bounds.size], 0.0); -// CGContextRef context = UIGraphicsGetCurrentContext(); - - float scaleFactor = [[UIScreen mainScreen] scale]; - CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - CGContextRef context = CGBitmapContextCreate(NULL, bounds.size.width * scaleFactor, bounds.size.height * scaleFactor, 8, 4 * bounds.size.width * scaleFactor, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); - CGContextScaleCTM(context, scaleFactor, scaleFactor); - - // Adjusts position and invert the image. - // The OpenGL uses the image data upside-down compared commom image files. - CGContextTranslateCTM(context, 0, bounds.size.height); - CGContextScaleCTM(context, 1.0, -1.0); + UIGraphicsBeginImageContextWithOptions(bounds.size, [self _bitmapOpaqueWithSize:bounds.size], 0.0); + CGContextRef context = UIGraphicsGetCurrentContext(); - CGColorSpaceRelease(colorSpace); +// float scaleFactor = [[UIScreen mainScreen] scale]; +// CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); +// CGContextRef context = CGBitmapContextCreate(NULL, bounds.size.width * scaleFactor, bounds.size.height * scaleFactor, 8, 4 * bounds.size.width * scaleFactor, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); +// CGContextScaleCTM(context, scaleFactor, scaleFactor); +// +// // Adjusts position and invert the image. +// // The OpenGL uses the image data upside-down compared commom image files. +// CGContextTranslateCTM(context, 0, bounds.size.height); +// CGContextScaleCTM(context, 1.0, -1.0); +// +// CGColorSpaceRelease(colorSpace); return context; } - (UIImage *)endDrawContext:(CGContextRef)context { -// UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); -// UIGraphicsEndImageContext(); + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); - CGImageRef imageRef= CGBitmapContextCreateImage(context); - UIImage *image = [[UIImage alloc] initWithCGImage:imageRef]; - CGContextRelease(context); +// CGImageRef imageRef= CGBitmapContextCreateImage(context); +// UIImage *image = [[UIImage alloc] initWithCGImage:imageRef]; +// CGContextRelease(context); return image; } @@ -216,20 +233,10 @@ - (WXDisplayBlock)_compositeDisplayBlock CGContextRef context = [self beginDrawContext:bounds]; -// float scaleFactor = [[UIScreen mainScreen] scale]; -// CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); -// CGContextRef context = CGBitmapContextCreate(NULL, bounds.size.width * scaleFactor, bounds.size.height * scaleFactor, 8, 4 * bounds.size.width * scaleFactor, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); -// CGContextScaleCTM(context, scaleFactor, scaleFactor); -// -// // Adjusts position and invert the image. -// // The OpenGL uses the image data upside-down compared commom image files. -// CGContextTranslateCTM(context, 0, bounds.size.height); -// CGContextScaleCTM(context, 1.0, -1.0); + UIGraphicsPushContext(context); [self _collectCompositingDisplayBlocks:displayBlocks context:context isCancelled:isCancelled]; - UIGraphicsPushContext(context); - for (dispatch_block_t block in displayBlocks) { if (isCancelled()) { UIGraphicsEndImageContext(); @@ -553,7 +560,7 @@ - (WXDisplayBlock)displayBlock - (WXDisplayCompletionBlock)displayCompletionBlock { - return nil; + return [self _displayCompletionBlock]; } @end diff --git a/ios/sdk/WeexSDK/Sources/Model/WXComponent.h b/ios/sdk/WeexSDK/Sources/Model/WXComponent.h index 7da7bc6a3f..bec45d107f 100644 --- a/ios/sdk/WeexSDK/Sources/Model/WXComponent.h +++ b/ios/sdk/WeexSDK/Sources/Model/WXComponent.h @@ -331,10 +331,6 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)setNeedsDisplay; -- (CGContextRef)beginDrawContext:(CGRect)bounds; - -- (UIImage *)endDrawContext:(CGContextRef)context; - /** * @abstract Returns a Boolean indicating whether the component needs to be drawn by `drawRect:` */ @@ -353,17 +349,29 @@ NS_ASSUME_NONNULL_BEGIN - (UIImage *)drawRect:(CGRect)rect; /** - * @abstract Called when a component finishes rendering its content. + * @abstract Called when a component finishes drawing its content. * @discussion Do not call this method directly. Weex calls this method at appropriate times to finish updating the component's content. * Subclasses can override this method to perform additional work on components that were rendered. */ -- (void)displayDidFinished:(BOOL)success; +- (void)didFinishDrawingLayer:(BOOL)success; /** * readyToRender, do not use it, will be deprecated soon */ - (void)readyToRender; +/** + * @abstract Creates a graphics context with the specified bounds, the context will be used for `drawRect:` in compositing environment + * @discussion You can override this method to use your own graphics context. + */ +- (CGContextRef)beginDrawContext:(CGRect)bounds; + +/** + * @abstract Removes the current graphics context and returns an image based on the contents of the current graphics context. + * @discussion You can override this method to use your own graphics context. The image will be set to layer, if your drawing system do not have layer and do not need image, returning nil is fine. + */ +- (UIImage *)endDrawContext:(CGContextRef)context; + @end @interface WXComponent (Deprecated) @@ -377,7 +385,7 @@ typedef void(^WXDisplayCompletionBlock)(CALayer *layer, BOOL finished); * @discussion The block returned will be called on any thread. * */ -- (WXDisplayBlock)displayBlock DEPRECATED_MSG_ATTRIBUTE("use drawRect method instead."); +- (WXDisplayBlock)displayBlock DEPRECATED_MSG_ATTRIBUTE("use drawRect: method instead."); /** * @abstract Return a block to be called while drawing is finished. @@ -385,7 +393,7 @@ typedef void(^WXDisplayCompletionBlock)(CALayer *layer, BOOL finished); * @discussion The block returned will be called on main thread. * */ -- (WXDisplayCompletionBlock)displayCompletionBlock DEPRECATED_MSG_ATTRIBUTE("use displayDidFinished method instead."); +- (WXDisplayCompletionBlock)displayCompletionBlock DEPRECATED_MSG_ATTRIBUTE("use didFinishDrawingLayer: method instead."); @end From a069edb7ad815893eb2e330307bca5c211ebacfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9A=90=E9=A3=8E?= Date: Thu, 6 Apr 2017 15:59:20 +0800 Subject: [PATCH 06/10] * [ios] Add public method to trigger display --- ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m | 5 +++++ ios/sdk/WeexSDK/Sources/Model/WXComponent.h | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m b/ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m index 0d4bca1023..a72e84d0fd 100644 --- a/ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m +++ b/ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m @@ -191,6 +191,11 @@ - (void)_willDisplayLayer:(CALayer *)layer } } +- (void)triggerDisplay +{ + [self _willDisplayLayer:_layer]; +} + - (CGContextRef)beginDrawContext:(CGRect)bounds { UIGraphicsBeginImageContextWithOptions(bounds.size, [self _bitmapOpaqueWithSize:bounds.size], 0.0); diff --git a/ios/sdk/WeexSDK/Sources/Model/WXComponent.h b/ios/sdk/WeexSDK/Sources/Model/WXComponent.h index bec45d107f..c2cc3b9a37 100644 --- a/ios/sdk/WeexSDK/Sources/Model/WXComponent.h +++ b/ios/sdk/WeexSDK/Sources/Model/WXComponent.h @@ -360,6 +360,11 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)readyToRender; +/** + * @abstract trigger display if you do not have a WXLayer + */ +- (void)triggerDisplay; + /** * @abstract Creates a graphics context with the specified bounds, the context will be used for `drawRect:` in compositing environment * @discussion You can override this method to use your own graphics context. From e3ee82fb483031404131eba65fd66f9eb8c10083 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9A=90=E9=A3=8E?= Date: Thu, 6 Apr 2017 16:03:51 +0800 Subject: [PATCH 07/10] * [ios] bring triggerDisplay to main thread --- ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m b/ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m index a72e84d0fd..9dbe63aa56 100644 --- a/ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m +++ b/ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m @@ -193,7 +193,9 @@ - (void)_willDisplayLayer:(CALayer *)layer - (void)triggerDisplay { - [self _willDisplayLayer:_layer]; + WXPerformBlockOnMainThread(^{ + [self _willDisplayLayer:_layer]; + }); } - (CGContextRef)beginDrawContext:(CGRect)bounds From aab1eefbbce4dbe3b47c555afe834a85a5b16cd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9A=90=E9=A3=8E?= Date: Sun, 9 Apr 2017 21:43:23 +0800 Subject: [PATCH 08/10] * [ios] support border-x-x-radius in image. --- .../Sources/Component/WXImageComponent.m | 44 ++++++++++++++++++- .../Sources/Display/WXComponent+Display.m | 2 +- .../WeexSDK/Sources/Display/WXRoundedRect.h | 2 + .../WeexSDK/Sources/Display/WXRoundedRect.mm | 5 +++ 4 files changed, 50 insertions(+), 3 deletions(-) diff --git a/ios/sdk/WeexSDK/Sources/Component/WXImageComponent.m b/ios/sdk/WeexSDK/Sources/Component/WXImageComponent.m index 73696fda03..974bd65f63 100644 --- a/ios/sdk/WeexSDK/Sources/Component/WXImageComponent.m +++ b/ios/sdk/WeexSDK/Sources/Component/WXImageComponent.m @@ -14,6 +14,8 @@ #import "WXType.h" #import "WXConvert.h" #import "WXURLRewriteProtocol.h" +#import "WXRoundedRect.h" +#import "UIBezierPath+Weex.h" @interface WXImageView : UIImageView @@ -154,6 +156,8 @@ - (void)viewDidLoad imageView.clipsToBounds = YES; imageView.exclusiveTouch = YES; + [self _clipsToBounds]; + [self updateImage]; } @@ -171,6 +175,16 @@ - (UIImage *)drawRect:(CGRect)rect; { if (!self.image) { [self updateImage]; + return nil; + } + + WXRoundedRect *borderRect = [[WXRoundedRect alloc] initWithRect:rect topLeft:_borderTopLeftRadius topRight:_borderTopRightRadius bottomLeft:_borderBottomLeftRadius bottomRight:_borderBottomRightRadius]; + + WXRadii *radii = borderRect.radii; + if ([radii hasBorderRadius]) { + CGFloat topLeft = radii.topLeft, topRight = radii.topRight, bottomLeft = radii.bottomLeft, bottomRight = radii.bottomRight; + UIBezierPath *bezierPath = [UIBezierPath wx_bezierPathWithRoundedRect:rect topLeft:topLeft topRight:topRight bottomLeft:bottomLeft bottomRight:bottomRight]; + [bezierPath addClip]; } return self.image; } @@ -182,6 +196,13 @@ - (void)viewWillUnload _image = nil; } +- (void)_frameDidCalculated:(BOOL)isChanged +{ + if ([self isViewLoaded] && isChanged) { + [self _clipsToBounds]; + } +} + - (void)setImageSrc:(NSString*)src { if (![src isEqualToString:_imageSrc]) { @@ -325,9 +346,28 @@ - (void)cancelImage return imageLoader; } -- (BOOL)_needsDrawBorder +- (void)_clipsToBounds { - return NO; + if (!_clipToBounds) { + return; + } + + WXRoundedRect *borderRect = [[WXRoundedRect alloc] initWithRect:self.view.bounds topLeft:_borderTopLeftRadius topRight:_borderTopRightRadius bottomLeft:_borderBottomLeftRadius bottomRight:_borderBottomRightRadius]; + // here is computed radii, do not use original style + WXRadii *radii = borderRect.radii; + + if ([radii radiusesAreEqual]) { + return; + } + + CGFloat topLeft = radii.topLeft, topRight = radii.topRight, bottomLeft = radii.bottomLeft, bottomRight = radii.bottomRight; + + // clip to border radius + UIBezierPath *bezierPath = [UIBezierPath wx_bezierPathWithRoundedRect:self.view.bounds topLeft:topLeft topRight:topRight bottomLeft:bottomLeft bottomRight:bottomRight]; + + CAShapeLayer *shapeLayer = [CAShapeLayer layer]; + shapeLayer.path = bezierPath.CGPath; + self.layer.mask = shapeLayer; } #ifdef UITEST diff --git a/ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m b/ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m index 9dbe63aa56..a02f53a9cb 100644 --- a/ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m +++ b/ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m @@ -555,7 +555,7 @@ - (BOOL)_bitmapOpaqueWithSize:(CGSize)size WXRoundedRect *borderRect = [[WXRoundedRect alloc] initWithRect:rect topLeft:_borderTopLeftRadius topRight:_borderTopRightRadius bottomLeft:_borderBottomLeftRadius bottomRight:_borderBottomRightRadius]; WXRadii *radii = borderRect.radii; BOOL hasBorderRadius = [radii hasBorderRadius]; - return (!hasBorderRadius) && CGColorGetAlpha(_backgroundColor.CGColor) == 1.0; + return (!hasBorderRadius) && _opacity == 1.0 && CGColorGetAlpha(_backgroundColor.CGColor) == 1.0; } #pragma mark - Deprecated diff --git a/ios/sdk/WeexSDK/Sources/Display/WXRoundedRect.h b/ios/sdk/WeexSDK/Sources/Display/WXRoundedRect.h index 4cde88ae33..a90099e50f 100644 --- a/ios/sdk/WeexSDK/Sources/Display/WXRoundedRect.h +++ b/ios/sdk/WeexSDK/Sources/Display/WXRoundedRect.h @@ -19,6 +19,8 @@ - (BOOL)hasBorderRadius; +- (BOOL)radiusesAreEqual; + @end @interface WXRoundedRect : NSObject diff --git a/ios/sdk/WeexSDK/Sources/Display/WXRoundedRect.mm b/ios/sdk/WeexSDK/Sources/Display/WXRoundedRect.mm index fb624656db..e02c5e1bc2 100644 --- a/ios/sdk/WeexSDK/Sources/Display/WXRoundedRect.mm +++ b/ios/sdk/WeexSDK/Sources/Display/WXRoundedRect.mm @@ -31,6 +31,11 @@ - (BOOL)hasBorderRadius return _topLeft > 0.001 || _topRight > 0.001 || _bottomLeft > 0.001 || _bottomRight > 0.001; } +- (BOOL)radiusesAreEqual +{ + return _topLeft == _topRight && _topRight == _bottomRight && _bottomRight == _bottomLeft; +} + - (void)scale:(float)factor { if (factor == 1) { From 4a7a54b3aed794774fd0e242ca1c2bd33e00d439 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9A=90=E9=A3=8E?= Date: Sun, 9 Apr 2017 22:05:10 +0800 Subject: [PATCH 09/10] + [example] Add compositing showcase --- examples/vue/index.vue | 10 ++- examples/vue/showcase/compositing.vue | 89 +++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 examples/vue/showcase/compositing.vue diff --git a/examples/vue/index.vue b/examples/vue/index.vue index e37931da4e..fb4693d7c0 100644 --- a/examples/vue/index.vue +++ b/examples/vue/index.vue @@ -6,8 +6,9 @@ module.exports = { data: function () { var root = typeof window === 'object' ? 'vue-web/vue' : 'vue' + return { - items: [ + items: [ // common {name: root + '/syntax/hello-world', title: 'Hello World'}, {name: root + '/style/index', title: 'Common Style'}, @@ -49,6 +50,13 @@ }, components: { exampleList: require('./include/example-list.vue') + }, + created: function() { + let root = typeof window === 'object' ? 'vue-web/vue' : 'vue' + let platform = this.$getConfig().env.platform.toLowerCase() + if (platform === 'ios') { + this.items.push({name: root + '/showcase/compositing', title: 'Compositing'}) + } } } diff --git a/examples/vue/showcase/compositing.vue b/examples/vue/showcase/compositing.vue new file mode 100644 index 0000000000..58e238f097 --- /dev/null +++ b/examples/vue/showcase/compositing.vue @@ -0,0 +1,89 @@ + + + + + + From 99b83db46fd46f6adf0c4d7ba36caa70749dfef2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9A=90=E9=A3=8E?= Date: Mon, 10 Apr 2017 11:13:58 +0800 Subject: [PATCH 10/10] * [ios] remove unused if --- ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m | 3 --- 1 file changed, 3 deletions(-) diff --git a/ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m b/ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m index a02f53a9cb..937339eddd 100644 --- a/ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m +++ b/ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m @@ -277,9 +277,6 @@ - (void)_collectCompositingDisplayBlocks:(NSMutableArray *)displayBlocks context if (shouldDisplay) { dispatch_block_t displayBlockToPush = ^{ - if (!context) { - - } CGContextSaveGState(context); CGContextTranslateCTM(context, frame.origin.x, frame.origin.y);