Использование NSImageView для отображения нескольких изображений в быстрой последовательности - PullRequest
2 голосов
/ 01 июля 2011

У меня есть приложение, в котором в одном окне есть NSImageView.Пользователь должен иметь возможность перетаскивать ЛЮБОЙ ФАЙЛ / ПАПКА (не только изображения) в представление изображений, поэтому я подклассифицировал класс NSImageView, чтобы добавить поддержку этих типов.

Причина, по которой я выбрал NSImageView вместонормальный вид - потому что я также хотел отобразить анимацию (скажем, стрелку, указывающую вниз и идущую вверх и вниз), когда пользователь наводит курсор мыши на файлы, готовые к сбросу.Мой вопрос заключается в следующем: каков наилучший способ (самый эффективный, самый быстрый, с наименьшим использованием процессора и т. Д.) Сделать это?

На самом деле, я уже сделал это, но что заставило меня задать этот вопрос:тот факт, что когда я устанавливаю изображения для изменения со скоростью ниже 0,02 сек, он начинает отставать.Вот как я это сделал:

В подклассе NSImageView:

  • есть ивар: NSTimer * animTimer;
  • переопределить awakeFromNib, вызвав [super awakeFromNib] изагрузка изображений в массив (около 45 изображений) с использованием NSImage
  • всякий раз, когда пользователь входит с файлами, запускает animTimer с частотой = 0,025 (меньше и отстает) и селектором, который устанавливает следующее изображение в массиве (называется drawNextImage)
  • всякий раз, когда пользователь завершает перетаскивание или завершает его, вызывает [animTimer invalidate], чтобы прекратить обновление изображений

Вот как я устанавливаю изображение в подклассе:

- (void)drawNextImage
{
    currentImageIndex++; // ivar / kNumberDNDImages is a constant defined as 46
    if (currentImageIndex >= kNumberDNDImages) { currentImageIndex = 0;}
    [super setImage: [imagesArray objectAtIndex: currentImageIndex]]; // imagesArray is ivar
}

Итак, как бы я сделал это достаточно быстро?Я бы хотел, чтобы частота составляла около 0,01 с, но с задержкой менее 0,025, так что это то, что я сейчас установил.О, и мои изображения имеют правильный размер (+ или - один пиксель или что-то), и они находятся в формате .png (мне нужна прозрачность - jpegs, например, не будет делать это).

РЕДАКТИРОВАТЬ:

Я попытался следовать предложению NSResponder и обновил свой метод следующим образом:

- (void)drawNextImage
{
    currentImageIndex++;
    if (currentImageIndex >= kNumberDNDImages) { currentImageIndex = 0;}
    NSRect smallImgRect;
    smallImgRect.origin = NSMakePoint(kSmallImageWidth * currentImageIndex, [self.bigDNDImage size].height); // Up left corner - ??
    smallImgRect.size = NSMakeSize(kSmallImageWidth, [self.bigDNDImage size].height);

    // Bottom left corner - ??
    NSPoint imgPoint = NSMakePoint(([self bounds].size.width - kSmallImageWidth) / 2, 0);

    [bigDNDImage drawAtPoint: imgPoint fromRect: smallImgRect operation: NSCompositeCopy fraction: 1];
}

Я также переместил этот метод и другие методы перетаскивания из подкласса NSImageView вПодкласс NSView у меня уже был.Все точно так же, за исключением суперкласса и этого метода.Я также изменил некоторые константы.

В моем раннем тестировании этого я получил несколько сообщений об ошибках / предупреждениях, которые не останавливали выполнение, говоря о NSGraphicsContext или о чем-то подобном.Теперь они исчезли, но вы точно знаете.Я понятия не имею, почему они появились и что они значат.Если они когда-нибудь появятся снова, я буду беспокоиться о них, а не сейчас:)

РЕДАКТИРОВАТЬ 2:

Это то, что я делаю сейчас:

- (void)drawNextImage
{
    currentImageIndex++;
    if (currentImageIndex >= kNumberDNDImages) { currentImageIndex = 0;}
    [self drawCurrentImage];
}
- (void)drawCurrentImage
{
    NSRect smallImgRect;
    smallImgRect.origin = NSMakePoint(kSmallImageWidth * currentImageIndex, 0); // Bottom left, for sure
    smallImgRect.size = NSMakeSize(kSmallImageWidth, [self.bigDNDImage size].height);

    // Bottom left as well
    NSPoint imgPoint = NSMakePoint(([self bounds].size.width - kSmallImageWidth) / 2, 0);

    [bigDNDImage drawAtPoint: imgPoint fromRect: smallImgRect operation: NSCompositeCopy fraction: 1];
}

Иподвох здесь в том, чтобы вызвать drawCurrentImage при вызове drawRect (видите, на самом деле это было проще решить, чем я думал).

Теперь я должен сказать, что я не пробовал это с составным изображением, потому что я не могНе могу найти хороший и быстрый способ объединить более 40 изображений так, как я хотел (одно рядом с другим).Но для тех, кто не заинтересован, я изменил это, чтобы сделать то же самое, что и мой подкласс NSImageView (чтение 40+ изображений из массива и их отображение), и я не обнаружил скачка скорости: NSView столь же отстает от 0,025, что и NSImageView.Также я обнаружил некоторые проблемы при использовании основной анимации (изображение рисуется в странных местах вместо того, где я говорю ей) и некоторые предупреждения, говорящие о NSGraphicsContext, который я вообще не знаю, как решить (я полностьюнуб, когда дело доходит до рисования и тому подобное с инструментами Objective-C).Поэтому в настоящее время я использую NSImageView, если я не найду способ объединить все эти изображения и попробовать его с NSView.

Ответы [ 2 ]

2 голосов
/ 03 июля 2011

Core Animation, вероятно, будет самой быстрой, так как она будет делать все на GPU. Создайте слой для каждого изображения, устанавливая каждый слой contents для CGImage, который вы можете сделать из каждого изображения, добавьте их все в качестве подслоев одного слоя верхнего уровня, размещайте слой верхнего уровня в простом NSView , а затем просто по очереди переключайте свойство hidden каждого слоя изображения.

0 голосов
/ 01 июля 2011

Я бы, вероятно, нарисовал все изображения компонентов в одно длинное изображение и нарисовал бы сегменты в виде, используя -drawAtPoint:fromRect:operation:fraction:.Я уверен, что вы могли бы сделать это быстрее, прибегнув к OpenGL.

...