Доступ к пикселям за пределами CVPixelBuffer, который был расширен с помощью Padding
Я пытаюсь расширить CVPixelBuffer так, чтобы доступ к памяти, находящейся вне буфера, не вызывал ошибку EXC_BAD_ACCESS путем повторной инициализации CVPixelBuffer с заполнением. Тем не менее, это не похоже на работу. Любые советы о том, что я делаю неправильно, будут с благодарностью.
let paddingLeft = abs(min(cropX, 0))
let paddingRight = max((cropX + cropWidth) - (srcWidth - 1), 0)
let paddingBottom = max((cropY + cropHeight) - (srcHeight - 1), 0)
let paddingTop = abs(min(cropY, 0))
let attr = [kCVPixelBufferExtendedPixelsLeftKey: paddingLeft*40 + 1 as CFNumber,
kCVPixelBufferExtendedPixelsTopKey: paddingTop*40 + 1 as CFNumber,
kCVPixelBufferExtendedPixelsRightKey: paddingRight*40 + 1 as CFNumber,
kCVPixelBufferExtendedPixelsBottomKey: paddingBottom*40 + 1 as CFNumber]
guard kCVReturnSuccess == CVPixelBufferCreateWithBytes(kCFAllocatorDefault, srcWidth, srcHeight, pixelFormat, srcData, srcBytesPerRow, nil, nil, attr as CFDictionary, &paddedSrcPixelBuffer) else {
print("failed to allocate a new padded pixel buffer")
return nil
}
С расширенным CVPixelBuffer доступ к данным вне CVPixelBuffer (например, когда x, y отрицателен или больше, чем ширина / высота буфера) должен определяться поведением на основе моего понимания. Тем не менее следующий фрагмент кода вылетает в последней строке внутри VImageScale_ARGB8888 с EXC_BAD_ACCESS Code 1.
Это, вероятно, означает, что данные, к которым осуществляется доступ, не отображаются.
guard let paddedSrcData = CVPixelBufferGetBaseAddress(paddedSrcPixelBuffer) else {
print("Error: could not get padded pixel buffer base address")
return nil
}
srcBuffer = vImage_Buffer(data: paddedSrcData.advanced(by: offset),
height: vImagePixelCount(cropHeight),
width: vImagePixelCount(cropWidth),
rowBytes: srcBytesPerRow)
let destBytesPerRow = scaleWidth*4
let destData = malloc(scaleHeight*destBytesPerRow)
var destBuffer = vImage_Buffer(data: destData,
height: vImagePixelCount(scaleHeight),
width: vImagePixelCount(scaleWidth),
rowBytes: destBytesPerRow)
let vImageFlags: vImage_Flags = vImage_Flags(kvImageEdgeExtend)
let error = vImageScale_ARGB8888(&srcBuffer, &destBuffer, nil, vImageFlags) // crashes here due to EXC_BAD_ACCESS Code: 1
Большое спасибо!
1 ответ
Вот модифицированная версия функции изменения размера, которая будет создавать дополнительный буфер путем копирования смежных разделов памяти во вновь выделенный буфер с правильной формой.
public func resizePixelBuffer(_ srcPixelBuffer: CVPixelBuffer,
cropX: Int,
cropY: Int,
cropWidth: Int,
cropHeight: Int,
scaleWidth: Int,
scaleHeight: Int) -> CVPixelBuffer? {
let flags = CVPixelBufferLockFlags(rawValue: 0)
let pixelFormat = CVPixelBufferGetPixelFormatType(srcPixelBuffer)
guard kCVReturnSuccess == CVPixelBufferLockBaseAddress(srcPixelBuffer, flags) else {
return nil
}
defer { CVPixelBufferUnlockBaseAddress(srcPixelBuffer, flags) }
guard let srcData = CVPixelBufferGetBaseAddress(srcPixelBuffer) else {
print("Error: could not get pixel buffer base address")
return nil
}
let srcHeight = CVPixelBufferGetHeight(srcPixelBuffer)
let srcWidth = CVPixelBufferGetWidth(srcPixelBuffer)
let srcBytesPerRow = CVPixelBufferGetBytesPerRow(srcPixelBuffer)
let offset = cropY*srcBytesPerRow + cropX*4
var srcBuffer: vImage_Buffer!
var paddedSrcPixelBuffer: CVPixelBuffer!
if (cropX < 0 || cropY < 0 || cropX + cropWidth > srcWidth || cropY + cropHeight > srcHeight) {
let paddingLeft = abs(min(cropX, 0))
let paddingRight = max((cropX + cropWidth) - (srcWidth - 1), 0)
let paddingBottom = max((cropY + cropHeight) - (srcHeight - 1), 0)
let paddingTop = abs(min(cropY, 0))
let paddedHeight = paddingTop + srcHeight + paddingBottom
let paddedWidth = paddingLeft + srcWidth + paddingRight
guard kCVReturnSuccess == CVPixelBufferCreate(kCFAllocatorDefault, paddedWidth, paddedHeight, pixelFormat, nil, &paddedSrcPixelBuffer) else {
print("failed to allocate a new padded pixel buffer")
return nil
}
guard kCVReturnSuccess == CVPixelBufferLockBaseAddress(paddedSrcPixelBuffer, flags) else {
return nil
}
guard let paddedSrcData = CVPixelBufferGetBaseAddress(paddedSrcPixelBuffer) else {
print("Error: could not get padded pixel buffer base address")
return nil
}
let paddedBytesPerRow = CVPixelBufferGetBytesPerRow(paddedSrcPixelBuffer)
for yIndex in paddingTop..<srcHeight+paddingTop {
let dstRowStart = paddedSrcData.advanced(by: yIndex*paddedBytesPerRow).advanced(by: paddingLeft*4)
let srcRowStart = srcData.advanced(by: (yIndex - paddingTop)*srcBytesPerRow)
dstRowStart.copyMemory(from: srcRowStart, byteCount: srcBytesPerRow)
}
let paddedOffset = (cropY + paddingTop)*paddedBytesPerRow + (cropX + paddingLeft)*4
srcBuffer = vImage_Buffer(data: paddedSrcData.advanced(by: paddedOffset),
height: vImagePixelCount(cropHeight),
width: vImagePixelCount(cropWidth),
rowBytes: paddedBytesPerRow)
} else {
srcBuffer = vImage_Buffer(data: srcData.advanced(by: offset),
height: vImagePixelCount(cropHeight),
width: vImagePixelCount(cropWidth),
rowBytes: srcBytesPerRow)
}
let destBytesPerRow = scaleWidth*4
guard let destData = malloc(scaleHeight*destBytesPerRow) else {
print("Error: out of memory")
return nil
}
var destBuffer = vImage_Buffer(data: destData,
height: vImagePixelCount(scaleHeight),
width: vImagePixelCount(scaleWidth),
rowBytes: destBytesPerRow)
let vImageFlags: vImage_Flags = vImage_Flags(kvImageEdgeExtend)
let error = vImageScale_ARGB8888(&srcBuffer, &destBuffer, nil, vImageFlags)
if error != kvImageNoError {
print("Error:", error)
free(destData)
return nil
}
let releaseCallback: CVPixelBufferReleaseBytesCallback = { _, ptr in
if let ptr = ptr {
free(UnsafeMutableRawPointer(mutating: ptr))
}
}
var dstPixelBuffer: CVPixelBuffer?
let status = CVPixelBufferCreateWithBytes(nil, scaleWidth, scaleHeight,
pixelFormat, destData,
destBytesPerRow, releaseCallback,
nil, nil, &dstPixelBuffer)
if status != kCVReturnSuccess {
print("Error: could not create new pixel buffer")
free(destData)
return nil
}
if paddedSrcPixelBuffer != nil {
CVPixelBufferUnlockBaseAddress(paddedSrcPixelBuffer, flags)
}
return dstPixelBuffer
}