Производительность Quartz2D — как улучшить

#ios

#iOS

Вопрос:

Я работаю в iOS и столкнулся с этой проблемой. Я буду получать относительно большой набор данных (массив размером 500×500 или больше в стиле C). Мне нужно построить из этих данных то, что по сути является графиком X / Y, где каждой точке данных в сетке 500×500 соответствует цвет, основанный на ее значении. Дело в том, что это меняется со временем, создавая, так сказать, некоторую анимацию, поэтому вычисления должны быть быстрыми, чтобы изменяться при поступлении нового набора данных.

Итак, по сути, для каждой точки в массиве мне нужно выяснить, какому цвету она должна соответствовать, затем определить квадрат, который нужно нарисовать на сетке для представления данных. Если бы моя сетка составляла 768×768 пикселей, но у меня был набор данных размером 500×500, то каждая точка данных представляла бы собой прямоугольник размером примерно 1,5×1,5 (он округлен, но я надеюсь, вы поняли идею).

Я попробовал это, создав новый класс view и переопределив drawRect. Однако это привело к ужасной производительности при чем-либо намного превышающем набор данных 20×20.

Я видел несколько предложений о записи в буферы изображений, но мне не удалось найти никаких примеров этого (я довольно новичок в iOS). У вас есть какие-либо предложения или не могли бы вы указать мне на какие-либо ресурсы, которые могли бы помочь?

Спасибо, что уделили мне время,

Дэррил

Ответ №1:

Вот некоторый код, который вы можете поместить в метод, который будет генерировать и возвращать UIImage в контексте вне экрана. Чтобы повысить производительность, попробуйте придумать способы минимизировать количество итераций, например, увеличить ваши «пиксели» или отрисовывать только ту часть, которая изменяется.

     UIGraphicsBeginImageContext(size); // Use your own image size here      

CGContextRef context = UIGraphicsGetCurrentContext();       

// push context to make it current 
// (need to do this manually because we are not drawing in a UIView)
//
UIGraphicsPushContext(context);                             

for (CGFloat x = 0.0; x<size.width; x =1.0) {
   for (CGFloat y=0.0; y< size.height; y =1.0) {
// Set your color here
      CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 1.0));
      CGContextFillRect(context, CGRectMake(x, y, 1.0, 1.0));
   }
}


// pop context 
//
UIGraphicsPopContext();     


// get a UIImage from the image context- enjoy!!!
//
UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext();
[outputImage retain];

// clean up drawing environment
//
UIGraphicsEndImageContext();
return [outputImage autorelease];
  

Комментарии:

1. Нет, он использовал drawRect в подклассе UIView, что было бы намного медленнее, чем рисование в контексте закадрового изображения.

2. Что делает это намного быстрее? Почему рисование в закадровом контексте намного дешевле? Я тоже предлагаю закадровый рендеринг, но с прямым доступом к памяти вместо выполнения 25 000 вызовов quartz, которые, я думаю (не измеренные), являются здесь бутылочным горлышком производительности.

3. Это сработало, спасибо! (Извините, я только сейчас возвращаюсь к этому).

Ответ №2:

Если вы хотите работать быстро, вам не следует вызывать явные функции рисования для каждого пикселя. Вы можете выделить память и использовать CGBitmapContextCreate для создания образа с данными в этой памяти, которая в основном представляет собой массив байтов. Выполните свои вычисления и запишите информацию о цвете (a-r-g-b) непосредственно в этот буфер. Однако вам придется самостоятельно рассчитать точность субпикселей (смешивание).

У меня нет примера под рукой, но поиск CGBitmapContextCreate должен направить вас в правильном направлении.