123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- //
- // YYAsyncLayer.m
- // YYKit <https://github.com/ibireme/YYKit>
- //
- // Created by ibireme on 15/4/11.
- // Copyright (c) 2015 ibireme.
- //
- // This source code is licensed under the MIT-style license found in the
- // LICENSE file in the root directory of this source tree.
- //
- #import "YYAsyncLayer.h"
- #import "YYSentinel.h"
- #if __has_include("YYDispatchQueuePool.h")
- #import "YYDispatchQueuePool.h"
- #else
- #import <libkern/OSAtomic.h>
- #endif
- /// Global display queue, used for content rendering.
- static dispatch_queue_t YYAsyncLayerGetDisplayQueue() {
- #ifdef YYDispatchQueuePool_h
- return YYDispatchQueueGetForQOS(NSQualityOfServiceUserInitiated);
- #else
- #define MAX_QUEUE_COUNT 16
- static int queueCount;
- static dispatch_queue_t queues[MAX_QUEUE_COUNT];
- static dispatch_once_t onceToken;
- static int32_t counter = 0;
- dispatch_once(&onceToken, ^{
- queueCount = (int)[NSProcessInfo processInfo].activeProcessorCount;
- queueCount = queueCount < 1 ? 1 : queueCount > MAX_QUEUE_COUNT ? MAX_QUEUE_COUNT : queueCount;
- if ([UIDevice currentDevice].systemVersion.floatValue >= 8.0) {
- for (NSUInteger i = 0; i < queueCount; i++) {
- dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INITIATED, 0);
- queues[i] = dispatch_queue_create("com.ibireme.yykit.render", attr);
- }
- } else {
- for (NSUInteger i = 0; i < queueCount; i++) {
- queues[i] = dispatch_queue_create("com.ibireme.yykit.render", DISPATCH_QUEUE_SERIAL);
- dispatch_set_target_queue(queues[i], dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
- }
- }
- });
- int32_t cur = OSAtomicIncrement32(&counter);
- if (cur < 0) cur = -cur;
- return queues[(cur) % queueCount];
- #undef MAX_QUEUE_COUNT
- #endif
- }
- static dispatch_queue_t YYAsyncLayerGetReleaseQueue() {
- #ifdef YYDispatchQueuePool_h
- return YYDispatchQueueGetForQOS(NSQualityOfServiceDefault);
- #else
- return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
- #endif
- }
- @implementation YYAsyncLayerDisplayTask
- @end
- @implementation YYAsyncLayer {
- YYSentinel *_sentinel;
- }
- #pragma mark - Override
- + (id)defaultValueForKey:(NSString *)key {
- if ([key isEqualToString:@"displaysAsynchronously"]) {
- return @(YES);
- } else {
- return [super defaultValueForKey:key];
- }
- }
- - (instancetype)init {
- self = [super init];
- static CGFloat scale; //global
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- scale = [UIScreen mainScreen].scale;
- });
- self.contentsScale = scale;
- _sentinel = [YYSentinel new];
- _displaysAsynchronously = YES;
- return self;
- }
- - (void)dealloc {
- [_sentinel increase];
- }
- - (void)setNeedsDisplay {
- [self _cancelAsyncDisplay];
- [super setNeedsDisplay];
- }
- - (void)display {
- super.contents = super.contents;
- [self _displayAsync:_displaysAsynchronously];
- }
- #pragma mark - Private
- - (void)_displayAsync:(BOOL)async {
- __strong id<YYAsyncLayerDelegate> delegate = (id)self.delegate;
- YYAsyncLayerDisplayTask *task = [delegate newAsyncDisplayTask];
- if (!task.display) {
- if (task.willDisplay) task.willDisplay(self);
- self.contents = nil;
- if (task.didDisplay) task.didDisplay(self, YES);
- return;
- }
-
- if (async) {
- if (task.willDisplay) task.willDisplay(self);
- YYSentinel *sentinel = _sentinel;
- int32_t value = sentinel.value;
- BOOL (^isCancelled)() = ^BOOL() {
- return value != sentinel.value;
- };
- CGSize size = self.bounds.size;
- BOOL opaque = self.opaque;
- CGFloat scale = self.contentsScale;
- CGColorRef backgroundColor = (opaque && self.backgroundColor) ? CGColorRetain(self.backgroundColor) : NULL;
- if (size.width < 1 || size.height < 1) {
- CGImageRef image = (__bridge_retained CGImageRef)(self.contents);
- self.contents = nil;
- if (image) {
- dispatch_async(YYAsyncLayerGetReleaseQueue(), ^{
- CFRelease(image);
- });
- }
- if (task.didDisplay) task.didDisplay(self, YES);
- CGColorRelease(backgroundColor);
- return;
- }
-
- dispatch_async(YYAsyncLayerGetDisplayQueue(), ^{
- if (isCancelled()) {
- CGColorRelease(backgroundColor);
- return;
- }
- UIGraphicsBeginImageContextWithOptions(size, opaque, scale);
- CGContextRef context = UIGraphicsGetCurrentContext();
- if (opaque && context) {
- CGContextSaveGState(context); {
- if (!backgroundColor || CGColorGetAlpha(backgroundColor) < 1) {
- CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
- CGContextAddRect(context, CGRectMake(0, 0, size.width * scale, size.height * scale));
- CGContextFillPath(context);
- }
- if (backgroundColor) {
- CGContextSetFillColorWithColor(context, backgroundColor);
- CGContextAddRect(context, CGRectMake(0, 0, size.width * scale, size.height * scale));
- CGContextFillPath(context);
- }
- } CGContextRestoreGState(context);
- CGColorRelease(backgroundColor);
- }
- task.display(context, size, isCancelled);
- if (isCancelled()) {
- UIGraphicsEndImageContext();
- dispatch_async(dispatch_get_main_queue(), ^{
- if (task.didDisplay) task.didDisplay(self, NO);
- });
- return;
- }
- UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
- UIGraphicsEndImageContext();
- if (isCancelled()) {
- dispatch_async(dispatch_get_main_queue(), ^{
- if (task.didDisplay) task.didDisplay(self, NO);
- });
- return;
- }
- dispatch_async(dispatch_get_main_queue(), ^{
- if (isCancelled()) {
- if (task.didDisplay) task.didDisplay(self, NO);
- } else {
- self.contents = (__bridge id)(image.CGImage);
- if (task.didDisplay) task.didDisplay(self, YES);
- }
- });
- });
- } else {
- [_sentinel increase];
- if (task.willDisplay) task.willDisplay(self);
- UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.opaque, self.contentsScale);
- CGContextRef context = UIGraphicsGetCurrentContext();
- if (self.opaque && context) {
- CGSize size = self.bounds.size;
- size.width *= self.contentsScale;
- size.height *= self.contentsScale;
- CGContextSaveGState(context); {
- if (!self.backgroundColor || CGColorGetAlpha(self.backgroundColor) < 1) {
- CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
- CGContextAddRect(context, CGRectMake(0, 0, size.width, size.height));
- CGContextFillPath(context);
- }
- if (self.backgroundColor) {
- CGContextSetFillColorWithColor(context, self.backgroundColor);
- CGContextAddRect(context, CGRectMake(0, 0, size.width, size.height));
- CGContextFillPath(context);
- }
- } CGContextRestoreGState(context);
- }
- task.display(context, self.bounds.size, ^{return NO;});
- UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
- UIGraphicsEndImageContext();
- self.contents = (__bridge id)(image.CGImage);
- if (task.didDisplay) task.didDisplay(self, YES);
- }
- }
- - (void)_cancelAsyncDisplay {
- [_sentinel increase];
- }
- @end
|