2013-01-04 6 views
12

iOS có CADisplayLink, trong Mac OS X có CVDisplayLink, nhưng tôi không thể tìm cách sử dụng nó, tất cả các ví dụ đều liên quan đến OpenGL.Phương án CADisplayLink cho Mac OS X

Tôi tạo ra tùy chỉnh này UIView và tôi muốn dịch nó vào một NSView

#import "StarView.h" 
#import <QuartzCore/QuartzCore.h> 

#define MAX_FPS (100.0) 
#define MIN_FPS (MAX_FPS/40.0) 
#define FRAME_TIME (1.0/MAX_FPS) 
#define MAX_CPF (MAX_FPS/MIN_FPS) 
#define aEPS (0.0001f) 

@implementation StarView 

@synthesize starImage = _starImage; 
@synthesize x, y; 

- (void)baseInit { 
    _starImage = nil; 
    CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self  selector:@selector(runLoop)]; 
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; 
} 

- (id)initWithFrame:(CGRect)frame 
{ 
self = [super initWithFrame:frame]; 
if (self) { 
    [self baseInit]; 
} 
return self; 
} 

- (id)initWithCoder:(NSCoder *)aDecoder { 
if ((self = [super initWithCoder:aDecoder])) { 
    [self baseInit]; 
} 
return self; 
} 

// Only override drawRect: if you perform custom drawing. 
// An empty implementation adversely affects performance during animation. 
- (void)drawRect:(CGRect)rect 
{ 
    [self.starImage drawAtPoint:CGPointMake(self.x, self.y)]; 
} 

-(void) cycle { 
    self.x += 5; 
    if (self.x > 230) { 
     self.x = 0; 
    } 
} 

- (void) runLoop { 
//NSLog(@"called"); 
static CFTimeInterval last_time = 0.0f; 
static float cycles_left_over = 0.0f; 
static float dt2 = 0.0f; 

float dropAnimRate = (2.1f/25.0f); 

CFTimeInterval current_time = CACurrentMediaTime(); 
float dt = current_time - last_time + cycles_left_over; 
dt2 += dt; 

[self setNeedsDisplay]; 

if (dt > (MAX_CPF * FRAME_TIME)) { 
    dt = (MAX_CPF * FRAME_TIME); 
} 
while (dt > FRAME_TIME) { 
    if (dt2 > (dropAnimRate - aEPS)){ 
     [self cycle]; 
     dt2 = 0.0f; 
    } 
    dt -= FRAME_TIME; 
} 

cycles_left_over = dt; 
last_time = current_time; 
} 

@end 

Phần mà tôi không thể dịch là này một

- (void)baseInit { 
    _starImage = nil; 
    CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self  selector:@selector(runLoop)]; 
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; 
} 

tôi biết rằng tôi có thể sử dụng một NSTimer, nhưng nó không có cùng độ chính xác

Trả lời

19

Bạn có thể định cấu hình CVDisplayLink để hoạt động độc lập với OpenGL. Sau đây là đoạn code mà tôi đã sử dụng để thiết lập một CVDisplayLink để kích hoạt chụp và cung cấp thường xuyên từ một camera công nghiệp:

CGDirectDisplayID displayID = CGMainDisplayID(); 
CVReturn   error = kCVReturnSuccess; 
error = CVDisplayLinkCreateWithCGDisplay(displayID, &displayLink); 
if (error) 
{ 
    NSLog(@"DisplayLink created with error:%d", error); 
    displayLink = NULL; 
} 
CVDisplayLinkSetOutputCallback(displayLink, renderCallback, (__bridge void *)self); 

chức năng renderCallback của tôi trông giống như sau:

static CVReturn renderCallback(CVDisplayLinkRef displayLink, 
           const CVTimeStamp *inNow, 
           const CVTimeStamp *inOutputTime, 
           CVOptionFlags flagsIn, 
           CVOptionFlags *flagsOut, 
           void *displayLinkContext) 
{ 
    return [(__bridge SPVideoView *)displayLinkContext renderTime:inOutputTime]; 
} 

Bạn sẽ phải thay thế ở trên bằng lớp của riêng bạn và phương thức gọi lại, tất nhiên. __bridge trong mã có hỗ trợ ARC, vì vậy bạn có thể không cần nó trong môi trường được tính toán theo cách thủ công.

Để bắt đầu chụp các sự kiện từ các liên kết hiển thị, bạn muốn sử dụng

CVDisplayLinkStart(displayLink); 

và ngừng

CVDisplayLinkStop(displayLink); 

Khi hoàn tất, hãy chắc chắn để làm sạch sau khi được tạo ra bằng cách sử dụng CVDisplayLink CVDisplayLinkRelease().

Nếu tôi có thể đưa ra nhận xét cuối cùng, lý do tại sao bạn thấy CVDisplayLink thường được ghép nối với OpenGL là bạn thường không muốn làm việc nhanh chóng trên màn hình bằng cách sử dụng Đồ họa cốt lõi. Nếu bạn cần làm hoạt hình gì đó với tốc độ 30-60 FPS, bạn sẽ muốn vẽ trực tiếp bằng cách sử dụng OpenGL hoặc sử dụng Core Animation và cho phép nó xử lý thời gian hoạt ảnh cho bạn. Bản vẽ đồ họa cốt lõi không phải là cách để tạo ra những thứ linh hoạt.

+0

´quay lại [(__bridge SPVideoView *) displayLinkContext renderTime: inOutputTime]; 'Tôi không hiểu dòng này, tôi đoán rằng thay vì SPVideoView tôi nên sử dụng MyView, nhưng tôi không hiểu tôi nên sử dụng gì thay vì renderTime . Bạn có thể giải thích cho tôi mục đích của các hàm này (cả renderCallback và renderTime)? – AR89

+0

@ AR89 - Bởi vì CVDisplayLink có chức năng gọi lại, không phải là phương thức, bạn có thể gọi lại phương thức thích hợp trong lớp của mình. Ở trên, lớp tôi đang sử dụng là SPVideoView và tôi có một phương thức lấy dấu thời gian inOutputTime. Bạn sẽ sử dụng dấu thời gian đó để xác định xem bạn có muốn kích hoạt cập nhật hiển thị chưa. Ngoài ra, tôi cũng có thể chỉ ra rằng bạn nên cẩn thận với các bản cập nhật hiển thị bằng cách sử dụng điều này, bởi vì lời gọi này xuất hiện trên một chuỗi nền, vì vậy bạn sẽ cần phải chắc chắn rằng bản vẽ của bạn xuất hiện trên chuỗi chính nếu sử dụng Core Graphics. –

+0

đây là một câu trả lời hay, nhưng điều quan trọng là bạn sẽ kết thúc với việc gọi lại được kích hoạt trên một luồng khác với nơi mà một khung nhìn sẽ được vẽ? tôi nghĩ rằng nó có thể được cho OpenGL rendering ít nhất. – jheriko

1

Tôi nghĩ rằng một NSTimer sẽ là con đường để đi đến đây. Tuyên bố của bạn "nó không có cùng độ chính xác", thật thú vị. Có lẽ những gì bạn đang nhầm lẫn cho một vấn đề chính xác là một vấn đề chế độ chạy vòng lặp. Nếu bạn thấy bộ hẹn giờ không kích hoạt khi bạn đang thực hiện những việc nhất định như mở menu hoặc kéo, bạn có thể chỉ cần thay đổi chế độ vòng lặp chạy mà bộ hẹn giờ đang chạy.

Một tùy chọn khác là tính toán hoạt ảnh của bạn vào thời gian thực tế mà nó được hiển thị, không phải trên sự khác biệt giả định kể từ lần trả lại cuối cùng. Làm như sau sẽ trực quan phóng đại bất kỳ sự chậm trễ trong rendering có thể được nếu không đi không được chú ý.

Ngoài NSTimer, bạn cũng có thể làm hẹn giờ trong GCD. Hãy xem ví dụ này: http://www.fieryrobot.com/blog/2010/07/10/a-watchdog-timer-in-gcd/

Chúng tôi sử dụng CVDisplayLink để hiển thị OpenGL bao gồm phát lại video. Tôi khá chắc chắn nó quá mức cần thiết cho những gì bạn đang cố gắng làm.