如何从CGImage获取3通道图像数据?解决CGContext创建报错
从CGImage提取3通道RGB数据时创建CGContext失败的问题
报错信息:
CGBitmapContextCreate: unsupported parameter combination: 7443 % 4!=0; bytes per row must be a multiple of bytes per pixel
运行代码:
// cgImage is defined let width = cgImage.width let height = cgImage.height let bytesPerPixel = 3 // RGB let bytesPerRow = bytesPerPixel * width let rawData = UnsafeMutablePointer<UInt8>.allocate(capacity: height * bytesPerRow) let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.noneSkipLast.rawValue) .union(.byteOrder32Little).rawValue guard let context = CGContext(data: rawData, width: width, height: height, bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: bitmapInfo) else { fatalError("Unable to create bitmap context.") } context.draw(cgImage, in: CGRect(x: 0, y: 0, width: width, height: height))
我原本认为CGImageAlphaInfo.noneSkipLast会忽略Alpha通道,仅记录3字节每像素的RGB数据,但显然它仍尝试按4字节每像素编码。我是否存在误解?该如何从CGImage获取仅3通道的图像数据?
问题原因与解决方法
问题原因
你确实存在两处关键误解:
- 字节序标志误用:
.byteOrder32Little是针对4字节像素格式(如RGBA)的字节序配置,和3字节RGB格式完全不兼容,搭配后会导致Core Graphics误判像素格式。 - 内存对齐要求:Core Graphics强制要求位图的每行字节数(
bytesPerRow)必须是4的倍数,当width * 3无法被4整除时(比如你的7443),就会触发报错。
解决方法
方案1:修正参数,创建合法的3通道RGB上下文
去掉错误的字节序标志,同时将bytesPerRow调整为4的倍数,读取数据时跳过每行末尾的填充字节即可:
// cgImage is defined let width = cgImage.width let height = cgImage.height let bytesPerPixel = 3 // RGB // 计算对齐到4字节倍数的bytesPerRow var bytesPerRow = bytesPerPixel * width bytesPerRow = (bytesPerRow + 3) & ~3 // 向上取整到最近的4的倍数 let rawData = UnsafeMutablePointer<UInt8>.allocate(capacity: height * bytesPerRow) // 仅使用noneSkipLast,不要搭配byteOrder32Little let bitmapInfo = CGImageAlphaInfo.noneSkipLast.rawValue guard let context = CGContext(data: rawData, width: width, height: height, bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: bitmapInfo) else { fatalError("Unable to create bitmap context.") } context.draw(cgImage, in: CGRect(x: 0, y: 0, width: width, height: height)) // 读取RGB数据时跳过每行的填充字节 for y in 0..<height { let rowStart = rawData + y * bytesPerRow for x in 0..<width { let pixelOffset = x * bytesPerPixel let r = rowStart[pixelOffset] let g = rowStart[pixelOffset + 1] let b = rowStart[pixelOffset + 2] // 处理RGB数据 } }
方案2:先创建4通道RGBA上下文,再手动提取RGB通道
如果不想处理内存填充字节,可以先创建带Alpha通道的标准上下文,之后遍历数据时只保留RGB部分:
// cgImage is defined let width = cgImage.width let height = cgImage.height let bytesPerPixel = 4 // RGBA let bytesPerRow = bytesPerPixel * width let rawData = UnsafeMutablePointer<UInt8>.allocate(capacity: height * bytesPerRow) let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Little.rawValue guard let context = CGContext(data: rawData, width: width, height: height, bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: bitmapInfo) else { fatalError("Unable to create bitmap context.") } context.draw(cgImage, in: CGRect(x: 0, y: 0, width: width, height: height)) // 提取纯RGB数据,忽略Alpha通道 let rgbData = UnsafeMutablePointer<UInt8>.allocate(capacity: height * width * 3) for y in 0..<height { let rgbaRowStart = rawData + y * bytesPerRow let rgbRowStart = rgbData + y * width * 3 for x in 0..<width { let rgbaOffset = x * bytesPerPixel let rgbOffset = x * 3 rgbRowStart[rgbOffset] = rgbaRowStart[rgbaOffset] // R rgbRowStart[rgbOffset + 1] = rgbaRowStart[rgbaOffset + 1] // G rgbRowStart[rgbOffset + 2] = rgbaRowStart[rgbaOffset + 2] // B } }
内容的提问来源于stack exchange,提问作者Soid
相关产品推荐
相关产品推荐

