Оптимизированная альтернатива CGContextDrawImage

В настоящее время я много работаю с CoreGraphics на OSX.

Я запустил Time Profiler над своим кодом и обнаружил, что самое большое зависание происходит в CGContextDrawImage. Это часть цикла, который вызывается много раз в секунду.

У меня нет способа оптимизировать этот код как таковой (поскольку он находится в библиотеках Apple), но мне интересно, есть ли более быстрая альтернатива или способ повысить скорость.

Я использую изображение CGContextDraw после некоторого кода режима наложения, такого как: CGContextSetBlendMode(context, kCGBlendModeDifference);, поэтому альтернативные реализации должны поддерживать смешивание.

Результаты профилирования времени:

3658.0ms   15.0%    0.0               CGContextDrawImage
3658.0ms   15.0%    0.0                ripc_DrawImage
3539.0ms   14.5%    0.0                 ripc_AcquireImage
3539.0ms   14.5%    0.0                  CGSImageDataLock
3539.0ms   14.5%    1.0                   img_data_lock
3465.0ms   14.2%    0.0                    img_interpolate_read
2308.0ms    9.4%    7.0                     resample_band
1932.0ms    7.9%    1932.0                   resample_byte_h_3cpp_vector
369.0ms     1.5%    369.0                    resample_byte_v_Ncpp_vector
1157.0ms    4.7%    2.0                     img_decode_read
1150.0ms    4.7%    8.0                      decode_data
863.0ms     3.5%    863.0                     decode_swap
267.0ms     1.0%    267.0                     decode_byte_8bpc_3

Обновление:

Фактический источник - это что-то вроде следующего:

/////////////////////////////////////////////////////////////////////////////////////////
- (CGImageRef)createBlendedImage:(CGImageRef)image
                     secondImage:(CGImageRef)secondImage
                       blendMode:(CGBlendMode)blendMode
{
    // Get the image width and height
    size_t width = CGImageGetWidth(image);
    size_t height = CGImageGetHeight(image);

    // Set the frame
    CGRect frame = CGRectMake(0, 0, width, height);

    // Create context with alpha channel
    CGContextRef context = CGBitmapContextCreate(NULL,
                                                 width,
                                                 height,
                                                 CGImageGetBitsPerComponent(image),
                                                 CGImageGetBytesPerRow(image),
                                                 CGImageGetColorSpace(image),
                                                 kCGImageAlphaPremultipliedLast);

    if (!context) {
        return nil;
    }

    // Draw the image inside the context
    CGContextSetBlendMode(context, kCGBlendModeCopy);
    CGContextDrawImage(context, frame, image);

    // Set the blend mode and draw the second image
    CGContextSetBlendMode(context, blendMode);
    CGContextDrawImage(context, frame, secondImage);

    // Get the masked image from the context
    CGImageRef blendedImage = CGBitmapContextCreateImage(context);
    CGContextRelease(context);

    return blendedImage;
}

/////////////////////////////////////////////////////////////////////////////////////////
- (CGImageRef)createImageTick
{
    // `self.image` and `self.previousImage` are two instance properties (CGImageRefs)

    // Create blended image (stage one)
    CGImageRef stageOne = [self createBlendedImage:self.image
                                       secondImage:self.previousImage
                                         blendMode:kCGBlendModeXOR];

    // Create blended image (stage two) if stage one image is 50% red
    CGImageRef stageTwo = nil;

    if ([self isImageRed:stageOne]) {
        stageTwo = [self createBlendedImage:self.image
                                secondImage:stageOne
                                  blendMode:kCGBlendModeSourceAtop];
    }

    // Release intermediate image
    CGImageRelease(stageOne);

    return stageTwo;
}

person Chris Nolet    schedule 22.07.2013    source источник
comment
Хорошо, что вы действительно профилировали код, прежде чем пытаться выполнить преждевременную оптимизацию. Однако, если ваш код работает медленно, я бы не стал винить в этом библиотеку — я полагаю, что вам нужно будет перепроектировать код (уменьшить сложность, изменив алгоритм или выполнив какое-либо другое умное улучшение), чтобы иметь он работает значительно быстрее.   -  person    schedule 22.07.2013
comment
Похоже, что некоторое время тратится на декодирование и замену байтов данных изображения. Вы должны попробовать и использовать оптимизированные для iphone / дробленые PNG, если это возможно.   -  person Mike Weller    schedule 22.07.2013
comment
Много времени тратится на повторную выборку изображения. Можно ли изменить размер изображения заранее, чтобы не пришлось делать это при рисовании? Вы рисуете одно и то же изображение несколько раз одного размера или рисуете разные изображения? Вы его анимируете?   -  person Jeremy Roman    schedule 22.07.2013
comment
@JeremyRoman и др. Большое спасибо за ваши комментарии. Я рисую одно и то же изображение пару раз за цикл в разных контекстах с разными фильтрами и комбинирую с новыми изображениями. Включает ли повторная выборка переключение с RGB на RGBA? Что я мог бы попытаться ускорить или устранить передискретизацию?   -  person Chris Nolet    schedule 22.07.2013
comment
Было бы полезно узнать больше о том, что на самом деле делает ваш код на высоком уровне. например Вы рисуете анимацию? Если да, изучали ли вы, будет ли Core Animation более эффективной для вашего варианта использования?   -  person Jeremy Roman    schedule 22.07.2013
comment
Ура @JeremyRoman - я добавил пример кода. Это не анимация как таковая, но я обрабатываю похожие изображения снова и снова много раз в секунду. В настоящее время я управляю обработкой изображений с помощью очереди GCD, которая фактически вызывает createImageTick.   -  person Chris Nolet    schedule 22.07.2013


Ответы (1)


@JeremyRoman и др. Большое спасибо за ваши комментарии. Я рисую одно и то же изображение пару раз за цикл в разных контекстах с разными фильтрами и комбинирую с новыми изображениями. Включает ли повторная выборка переключение с RGB на RGBA? Что я мог бы попытаться ускорить или устранить передискретизацию? — Крис Нолет

Это то, для чего предназначен Core Image. См. Руководство по программированию основного образа для получения подробной информации. CGContext предназначен для рендеринга окончательных изображений на экран, что, похоже, не является вашей целью с каждым изображением, которое вы создаете.

person Rob Napier    schedule 22.07.2013
comment
На самом деле я буду записывать многие из этих образов на диск (в быстрой последовательности, на виртуальный RAM-диск). Лучше перенести обработку изображения в Core Image, а затем использовать createCGImage:fromRect: в сочетании с CGImageDestinationRef для записи, или это гарантирует, что все время будет использоваться компьютерная графика? - person Chris Nolet; 23.07.2013
comment
См. сессию 509 WWDC 2013 (Основные эффекты изображения и методы). Core Image лучше всего работает, когда вы можете хранить данные на графическом процессоре как можно дольше. Перемещение данных в GPU и обратно обходится дорого. Поэтому в идеале вы хотите переместить данные в графический процессор, применить множество фильтров, а затем отобразить их непосредственно на экране (быстрее) или обратно в память процессора (медленнее). Настоящим ключом к производительности является поддержание заполнения конвейера, поэтому вам необходимо изучить, как данные перемещаются между ЦП и ГП. См. также developer.apple.com/library /mac/#documentation/Графические изображения/ - person Rob Napier; 23.07.2013
comment
Также стоит проверить github.com/BradLarson/GPUImage: быстрее, чем CIImage, и проще (быстрее) переходы на и из CGImage. - person Chris Nolet; 02.08.2013