Подтвердить что ты не робот

Альтернатива CADisplayLink для Mac OS X

Является ли iOS CADisplayLink, в Mac OS X есть CVDisplayLink, но я не могу найти способ его использовать, все примеры связаны с OpenGL.

Я создал этот пользовательский UIView, и я хочу перевести его в 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

Часть, которую я не могу перевести, - это

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

Я знаю, что я могу использовать NSTimer, но он не имеет такой же точности

4b9b3361

Ответ 1

Вы можете настроить CVDisplayLink для работы независимо от OpenGL. Ниже приведен код, который я использовал для создания CVDisplayLink для запуска регулярного захвата и рендеринга с промышленной камеры:

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

Функция my renderCallback выглядит примерно так:

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

Разумеется, вам придется заменить вышеописанный свой класс и метод обратного вызова. __bridge в коде есть для поддержки ARC, поэтому вам может не понадобиться это в среде с подсчетом вручную.

Чтобы начать запись событий из ссылки на изображение, вы должны использовать

CVDisplayLinkStart(displayLink);

и остановить

CVDisplayLinkStop(displayLink);

Когда закончите, обязательно очистите после созданного CVDisplayLink с помощью CVDisplayLinkRelease().

Если я могу сделать последний комментарий, причина, по которой вы видите CVDisplayLink, обычно в паре с OpenGL, заключается в том, что вы обычно не хотите делать быстрые обновления на экране с помощью Core Graphics. Если вам нужно что-то оживить со скоростью 30-60 FPS, вам нужно либо рисовать напрямую, используя OpenGL, либо использовать Core Animation и позволить ему обрабатывать время анимации для вас. Чертеж Core Graphics - это не способ оживить вещи.

Ответ 2

Я бы подумал, что NSTimer будет здесь. Ваше выражение "не имеет такой же точности", интересно. Возможно, вы ошибаетесь в проблеме точности, это проблема режима цикла. Если вы видите, что таймер не срабатывает, когда вы выполняете определенные действия, такие как открытие меню или перетаскивание, вам может потребоваться просто изменить режимы цикла запуска, в которых работает таймер.

Другим вариантом является вычисление вашей анимации на основе фактического времени ее рендеринга, а не предполагаемой разницы с момента последнего прохода визуализации. Выполнение последнего визуально преувеличивает любые задержки в рендеринге, которые в противном случае могут остаться незамеченными.

Помимо NSTimer, вы также можете делать таймеры в GCD. Взгляните на этот пример: http://www.fieryrobot.com/blog/2010/07/10/a-watchdog-timer-in-gcd/

Мы используем CVDisplayLink для рендеринга OpenGL, который включает воспроизведение видео. Я почти уверен, что это слишком много для того, что вы пытаетесь сделать.