vImage выбрасывает EXC_BAD_ACCESS при работе с буферами - PullRequest
0 голосов
/ 30 марта 2019

Я использую код ниже для изменения размера изображения с помощью Accelerate / vImage.Он отлично работает для большинства изображений.Однако, когда операция изменения размера выполняется на довольно маленьком (441 байт, 80px x 48px) изображении JPEG, закодированном в формате оттенков серого, метод vImageScale_ARGB8888 выдает EXC_BAD_ACCESS.

.выделяется для данных назначения.Чтобы рассчитать необходимую емкость для буфера назначения, нам нужно взять:

destination_image_height * destination_image_width * bytes_per_pixel

, который используется в:

UnsafeMutablePointer<UInt8>.allocate(capacity: destHeight * destBytesPerRow)

где destBytesPerRow равно destWidth * bytesPerPixel

Я пытаюсь обработать один файл JPEG, закодированный в формате Grayscale, и изменить его размер наполовину до 40px x 24px.Изображения в градациях серого имеют ровно один байт на пиксель .Таким образом, у нас есть 24 x 40 * 1 в формуле для расчета необходимой мощности.Это ужасно не работает с EXC_BAD_ACCESS.

Однако, когда я увеличиваю необходимую емкость и устанавливаю 4 как bytesPerPixel, как это было бы с обычными изображениями RGB (A), код работает очень хорошо (24 * 40 * 4)

Я пытаюсь понять причину этого странного поведения.Почему переоценка необходимой емкости решает проблему?

Если это поможет, malloc_error_break сообщает следующее:

libsystem_malloc.dylib`malloc_error_break:
->  0x7fff694070de <+0>:  pushq  %rbp
    0x7fff694070df <+1>:  movq   %rsp, %rbp
    0x7fff694070e2 <+4>:  nop    
    0x7fff694070e3 <+5>:  nopl   (%rax)
    0x7fff694070e7 <+9>:  popq   %rbp
    0x7fff694070e8 <+10>: retq   

А вот мой полный код:

// Define format
var format = vImage_CGImageFormat(
    bitsPerComponent: 8,
    bitsPerPixel: 32,
    colorSpace: nil,
    bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.first.rawValue),
    version: 0,
    decode: nil,
    renderingIntent: CGColorRenderingIntent.defaultIntent
)

// Create empty input vImage buffer
var srcBuffer = vImage_Buffer()

// Fill the input buffer with inputImage pixel data
var error = vImageBuffer_InitWithCGImage(&srcBuffer, &format, nil, inputImage, vImage_Flags(kvImageNoFlags))

guard error == kvImageNoError else {
    free(srcBuffer.data)
    return nil
}

let destWidth = Int(width)
let destHeight = Int(height)

// ----
let bytesPerPixel = inputImage.bitsPerPixel / 8 //this does not work
//let bytesPerPixel = 4 //this works just fine
// ----

let destBytesPerRow = destWidth * bytesPerPixel

// Fill the input buffer with inputImage pixel data
let destData = UnsafeMutablePointer<UInt8>.allocate(capacity: destHeight * destBytesPerRow)

defer {
    destData.deinitialize(count: destHeight * destBytesPerRow)
    destData.deallocate()
}

var destBuffer = vImage_Buffer(
    data: destData,
    height: vImagePixelCount(destHeight),
    width: vImagePixelCount(destWidth),
    rowBytes: destBytesPerRow
)

// Perform the actual resize
error = vImageScale_ARGB8888(&srcBuffer, &destBuffer, nil, vImage_Flags(kvImageHighQualityResampling))

guard error == kvImageNoError else {
    return nil
}

// Convert vImage output buffer back into CGImage
let result = vImageCreateCGImageFromBuffer(
    &destBuffer,
    &format,
    nil,
    nil,
    vImage_Flags(kvImageNoFlags),
    &error
    )?.takeRetainedValue()

guard error == kvImageNoError else {
    return nil
}

return result
...