Я использую код ниже для изменения размера изображения с помощью 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