vImageAlphaBlend аварийно завершает работу

Я пытаюсь альфа-смешивать некоторые layers: [CGImageRef] в подпрограмме drawLayer(thisLayer: CALayer!, inContext ctx: CGContext!) моего пользовательского NSView. До сих пор я использовал CGContextDrawImage() для рисования этих слоев в контексте drawLayer. Во время профилирования я заметил, что CGContextDrawImage() требует 70% процессорного времени, поэтому я решил попробовать фреймворк Accelerate. Я изменил код, но он просто вылетает, и я понятия не имею, в чем может быть причина.

Я создаю эти слои следующим образом:

func addLayer() {
    let colorSpace: CGColorSpaceRef = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB)
    let bitmapInfo = CGBitmapInfo(CGImageAlphaInfo.PremultipliedFirst.rawValue)

    var layerContext = CGBitmapContextCreate(nil, UInt(canvasSize.width), UInt(canvasSize.height), 8, UInt(canvasSize.width * 4), colorSpace, bitmapInfo)
    var newLayer = CGBitmapContextCreateImage(layerContext)

    layers.append( newLayer )
}

Моя процедура drawLayers выглядит так:

override func drawLayer(thisLayer: CALayer!, inContext ctx: CGContext!)
{
    var ctxImageBuffer = vImage_Buffer(data:CGBitmapContextGetData(ctx),
        height:CGBitmapContextGetHeight(ctx),
        width:CGBitmapContextGetWidth(ctx),
        rowBytes:CGBitmapContextGetBytesPerRow(ctx))
    
    for imageLayer in layers
    {
        //CGContextDrawImage(ctx, CGRect(origin: frameOffset, size: canvasSize), imageLayer)
        
        var inProvider:CGDataProviderRef = CGImageGetDataProvider(imageLayer)
        var inBitmapData:CFDataRef = CGDataProviderCopyData(inProvider)
        var buffer:vImage_Buffer = vImage_Buffer(data: &inBitmapData, height:
            CGImageGetHeight(imageLayer), width: CGImageGetWidth(imageLayer), rowBytes:
            CGImageGetBytesPerRow(imageLayer))
            
        vImageAlphaBlend_ARGB8888(&buffer, &ctxImageBuffer, &ctxImageBuffer, 0)
    }
}

CanvasSize всегда один и тот же, а также все слои имеют одинаковый размер, поэтому я не понимаю, почему последняя строка вылетает.

Также я не вижу, как использовать новые удобные функции для создания vImageBuffers непосредственно из CGLayerRefs. Вот почему я делаю это сложным способом.

Любая помощь приветствуется.

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

inBitmapData действительно содержит данные о пикселях, которые отражают установленный мной цвет фона. Однако отладчик не может po &inBitmapData и выдает следующее сообщение:

ошибка: ссылка на 'CFData' не используется для инициализации параметра inout &inBitmapData

Поэтому я искал способ получить указатель на inBitmapData. Вот что я придумал:

var bitmapPtr: UnsafeMutablePointer<CFDataRef> = UnsafeMutablePointer<CFDataRef>.alloc(1)
bitmapPtr.initialize(inBitmapData)

Мне также пришлось изменить способ указания моих данных для обоих буферов, которые мне нужны для ввода альфа-смешивания. Теперь он больше не падает, и, к счастью, прирост скорости можно проверить с помощью профилировщика (vImageAlphaBlend занимает около трети от CGContextDrawImage), но, к сожалению, изображение приводит к прозрачному изображению с ошибками пикселей вместо белого фона изображения.

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


person Enie    schedule 31.01.2015    source источник


Ответы (1)


vImage_Buffer.data должен указывать на данные CFData (данные пикселей), а не на CFDataRef.

Кроме того, не все изображения хранят свои данные как четырехканальные, 8-битные данные на канал. Если он окажется трехканальным, или RGBA, или монохромным, вы можете получить более сбойные или забавные цвета. Кроме того, вы предположили, что необработанные данные изображения не умножаются предварительно, что может быть небезопасным предположением.

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

Некоторые вызовы CG возвращаются к vImage для выполнения работы. В таких случаях переписывание кода таким образом может оказаться невыгодным. Обычно правильно сначала внимательно посмотреть на обратные следы в вызове компьютерной графики, чтобы попытаться понять, почему вы затрачиваете на него так много работы. Часто ответом является преобразование цветового пространства. Я бы внимательно посмотрел на CGBitmapInfo и цветовое пространство поверхности рисования и ваших изображений и посмотрел, не могу ли я что-то сделать, чтобы они немного лучше совпадали.

IIRC, CALayerRefs обычно хранят свои данные в некэшируемом хранилище для лучшего доступа к графическому процессору. Это может вызвать проблемы для процессора. Если данные находятся в CALayerRef, я бы использовал CA для компоновки. Кроме того, я думал, что CALayer почти всегда являются 8-битным предварительным умножением BGRA. Если вы не собираетесь использовать CA для компоновки, то правильная функция vImage, вероятно, vImagePremultipliedAlphaBlend_RGBA/BGRA8888.

person Ian Ollmann    schedule 02.02.2015
comment
Прежде всего, спасибо за вашу помощь, к сожалению, я не смог решить свою проблему с ней. CFDataRef - это псевдоним типа CFData, поэтому он не должен вызывать сбоев, я все равно изменил его на CFData. Все мои слои созданы с помощью функции addLayer и поэтому имеют те же четыре канала с предварительно умноженной альфой. Прежде чем опубликовать свою проблему, я использовал RGBA (.PremultipliedLast), но быстрый компилятор не распознал макрос vImagePremultipliedAlphaBlend_RGBA8888, поэтому я переключился на создание своих слоев с помощью .PremultipliedFirst, а также безуспешно пробовал использовать vImagePremultipliedAlphaBlend_ARGB8888. - person Enie; 03.02.2015
comment
Кроме того, что касается функции vImageBuffer_initWithCGImage, не могли бы вы указать, как ее использовать? я уже нашел вопрос об этом здесь: " title="фатальная ошибка неожиданно обнаружила nil при развертывании необязательного значения, пока u"> stackoverflow.com/questions/26755978/ Последнее решение рекомендует создать структуру vImageBuffer с nil в качестве данных. Я не совсем уверен, может ли vImageBuffer_initWithCGImage записывать данные CGImage туда, куда указывает nil. Когда я пытаюсь создать CGImage из этого vImageBuffer с помощью vImageCreateCGImageFromBuffer, программа вылетает при попытке с ошибкой неправильного доступа. - person Enie; 03.02.2015
comment
Я не уверен в Swift, но когда вы делаете &inBitmapData, возвращает ли это указатель на CFDataRef или возвращает CFDataGetBytePtr(inBitmapData). В первом случае у вас может произойти сбой, потому что vImage ожидает указатель на пиксели, а не указатель на объект CFDataRef. В любом случае vImageBuffer_initWithCGImage и vImageBuffer_Init берут выделенный, но неинициализированный vImage_Buffer и заполняют его содержимое. Они также выделяют хранилище для vImage_Buffer.data. Вам нужно будет освободить (buffer.data), когда вы закончите с этим. (Все еще интересно, почему вы компонуете слои CA в CG) - person Ian Ollmann; 03.02.2015
comment
Содержимое моих слоев CGImage постоянно изменяется. Насколько я понимаю, представление с поддержкой слоя не подходит для этой цели. Поэтому я решил использовать представление хостинга слоев и нарисовать все свои CGImages в одном контексте CALayer. это самое быстрое решение, которое я придумал на данный момент, но я все же хотел бы все закрепить. Оператор 'inout' в &inBitmapData дает мне UnsafeMutablePointer, который запрашивает функция. CFDataGetBytePtr возвращает только UnsafePointer, что, кстати, также доставляет мне трудности, когда я пытаюсь заставить CFData управлять пикселями вручную. - person Enie; 03.02.2015
comment
Используйте отладчик для проверки памяти, на которую указывает &inBitmapData. Похоже на пиксели? Как правило, каждый четвертый байт для 8-битного четырехканального формата будет равен 0xff для альфа-канала. - person Ian Ollmann; 05.02.2015
comment
спасибо, что помогли найти проблему, я обновлю первоначальный вопрос. - person Enie; 06.02.2015