当使用 CGContext 缩放到小尺寸时,CGImage 的像素数据不完整
Pixel data of CGImage is incomplete when scaled with CGContext to small size
我有以下尺寸为 512x512 的透明背景上的白色圆圈的 png 图像:
在 Stack Overflow 上看不到这里,所以又出现了,以 UIImageView
黑色背景显示:
我正在尝试使用高质量插值和抗锯齿将图像缩小到不同尺寸,然后读取像素信息并继续使用它。
缩放是使用常规 CGContext
:
private func scaleImageAntialiased(_ cgImage: CGImage, _ size: CGSize) -> CGImage {
UIGraphicsBeginImageContext(size)
guard let context = UIGraphicsGetCurrentContext() else { fatalError() }
context.interpolationQuality = .high
context.setShouldAntialias(true)
context.draw(cgImage, in: CGRect(origin: .zero, size: size))
guard let image = UIGraphicsGetImageFromCurrentImageContext()?.cgImage else { fatalError() }
return image
}
然后我使用dataProvider
和CFDataGetBytePtr
读取像素信息:
var image = UIImage(named: "Round_512")!.cgImage!
let width: size_t = image.width
let height: size_t = image.height
let cfData = image.dataProvider!.data!
let dataPtr = CFDataGetBytePtr(cfData)
let bytesPerPixel = 4
var data: [GLubyte] = Array(UnsafeBufferPointer(start: dataPtr, count: width * height * bytesPerPixel))
一切正常,直到我尝试将新尺寸设置为 4x4px 或更小。
let size = 4
let newSize = CGSize(width: size, height: size)
image = scaleImageAntialiased(image, newSize)
虽然缩放后的图像仍然显示在 UIImageView
内部,正如您所期望的那样
原始 RGBA 字节数据缺少两行信息:
[3, 3, 3, 3, 75, 75, 75, 75, 74, 74, 74, 74, 3, 3, 3, 3,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
75, 75, 75, 75, 254, 254, 254, 254, 254, 254, 254, 254, 74, 74, 74, 74,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
(我对 print(data)
的上述结果进行了格式化,以使其更加明显)。如您所见,第二行和第四行字节 return 0,而不是与其他两行具有相同的值(对称)。
只有当我首先使用 CGContext
缩放图像然后尝试使用上述方法读取它时才会出现此问题。如果我拍摄缩放后的图像,将其保存到一个文件中,然后从该 4x4 文件中读取像素信息(使用相同的方法),我将得到预期的结果。
因此缩放本身有效。像素的读取也有效。只有组合没有。
谁能解释为什么会这样?
为了完整起见,这里有一个 download link 的 512 像素图像和一个完整的 Playground 文件,您可以使用它来重现该问题:
import UIKit
import GLKit
import PlaygroundSupport
private func scaleImageAntialiased(_ cgImage: CGImage, _ size: CGSize) -> CGImage {
UIGraphicsBeginImageContext(size)
guard let context = UIGraphicsGetCurrentContext() else { fatalError() }
context.interpolationQuality = .high
context.setShouldAntialias(true)
context.draw(cgImage, in: CGRect(origin: .zero, size: size))
guard let image = UIGraphicsGetImageFromCurrentImageContext()?.cgImage else { fatalError() }
return image
}
var image = UIImage(named: "Round_512")!.cgImage!
let size = 4
let newSize = CGSize(width: size, height: size)
image = scaleImageAntialiased(image, newSize)
let width: size_t = image.width
let height: size_t = image.height
let cfData = image.dataProvider!.data!
let dataPtr = CFDataGetBytePtr(cfData)
let bytesPerPixel = 4
var data: [GLubyte] = Array(UnsafeBufferPointer(start: dataPtr, count: width * height * bytesPerPixel))
print(data)
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
imageView.image = UIImage(cgImage: image)
imageView.backgroundColor = .black
PlaygroundPage.current.liveView = imageView
// Save the test image
let path = playgroundSharedDataDirectory.appendingPathComponent("Saved.png")
let pngData = UIImagePNGRepresentation(imageView.image!)
//try! pngData?.write(to: path)
我相信 CGImage
使用 32 bytesPerRow 的倍数...如果您只有 4 个像素,每个像素由 4 个字节表示,则每行的数据将为 "padded out"(填充数据为忽略)。
如果您检查图像:
print(image.bytesPerRow)
你会看到它打印 32
而不是 16
。
您想使用:
let bytesPerRow = image.bytesPerRow
var data: [GLubyte] = Array(UnsafeBufferPointer(start: dataPtr, count: height * bytesPerRow))
所以 height * bytesPerRow
而不是 width * height * bytesPerPixel
我有以下尺寸为 512x512 的透明背景上的白色圆圈的 png 图像:
在 Stack Overflow 上看不到这里,所以又出现了,以 UIImageView
黑色背景显示:
我正在尝试使用高质量插值和抗锯齿将图像缩小到不同尺寸,然后读取像素信息并继续使用它。
缩放是使用常规 CGContext
:
private func scaleImageAntialiased(_ cgImage: CGImage, _ size: CGSize) -> CGImage {
UIGraphicsBeginImageContext(size)
guard let context = UIGraphicsGetCurrentContext() else { fatalError() }
context.interpolationQuality = .high
context.setShouldAntialias(true)
context.draw(cgImage, in: CGRect(origin: .zero, size: size))
guard let image = UIGraphicsGetImageFromCurrentImageContext()?.cgImage else { fatalError() }
return image
}
然后我使用dataProvider
和CFDataGetBytePtr
读取像素信息:
var image = UIImage(named: "Round_512")!.cgImage!
let width: size_t = image.width
let height: size_t = image.height
let cfData = image.dataProvider!.data!
let dataPtr = CFDataGetBytePtr(cfData)
let bytesPerPixel = 4
var data: [GLubyte] = Array(UnsafeBufferPointer(start: dataPtr, count: width * height * bytesPerPixel))
一切正常,直到我尝试将新尺寸设置为 4x4px 或更小。
let size = 4
let newSize = CGSize(width: size, height: size)
image = scaleImageAntialiased(image, newSize)
虽然缩放后的图像仍然显示在 UIImageView
原始 RGBA 字节数据缺少两行信息:
[3, 3, 3, 3, 75, 75, 75, 75, 74, 74, 74, 74, 3, 3, 3, 3,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
75, 75, 75, 75, 254, 254, 254, 254, 254, 254, 254, 254, 74, 74, 74, 74,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
(我对 print(data)
的上述结果进行了格式化,以使其更加明显)。如您所见,第二行和第四行字节 return 0,而不是与其他两行具有相同的值(对称)。
只有当我首先使用 CGContext
缩放图像然后尝试使用上述方法读取它时才会出现此问题。如果我拍摄缩放后的图像,将其保存到一个文件中,然后从该 4x4 文件中读取像素信息(使用相同的方法),我将得到预期的结果。
因此缩放本身有效。像素的读取也有效。只有组合没有。
谁能解释为什么会这样?
为了完整起见,这里有一个 download link 的 512 像素图像和一个完整的 Playground 文件,您可以使用它来重现该问题:
import UIKit
import GLKit
import PlaygroundSupport
private func scaleImageAntialiased(_ cgImage: CGImage, _ size: CGSize) -> CGImage {
UIGraphicsBeginImageContext(size)
guard let context = UIGraphicsGetCurrentContext() else { fatalError() }
context.interpolationQuality = .high
context.setShouldAntialias(true)
context.draw(cgImage, in: CGRect(origin: .zero, size: size))
guard let image = UIGraphicsGetImageFromCurrentImageContext()?.cgImage else { fatalError() }
return image
}
var image = UIImage(named: "Round_512")!.cgImage!
let size = 4
let newSize = CGSize(width: size, height: size)
image = scaleImageAntialiased(image, newSize)
let width: size_t = image.width
let height: size_t = image.height
let cfData = image.dataProvider!.data!
let dataPtr = CFDataGetBytePtr(cfData)
let bytesPerPixel = 4
var data: [GLubyte] = Array(UnsafeBufferPointer(start: dataPtr, count: width * height * bytesPerPixel))
print(data)
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
imageView.image = UIImage(cgImage: image)
imageView.backgroundColor = .black
PlaygroundPage.current.liveView = imageView
// Save the test image
let path = playgroundSharedDataDirectory.appendingPathComponent("Saved.png")
let pngData = UIImagePNGRepresentation(imageView.image!)
//try! pngData?.write(to: path)
我相信 CGImage
使用 32 bytesPerRow 的倍数...如果您只有 4 个像素,每个像素由 4 个字节表示,则每行的数据将为 "padded out"(填充数据为忽略)。
如果您检查图像:
print(image.bytesPerRow)
你会看到它打印 32
而不是 16
。
您想使用:
let bytesPerRow = image.bytesPerRow
var data: [GLubyte] = Array(UnsafeBufferPointer(start: dataPtr, count: height * bytesPerRow))
所以 height * bytesPerRow
而不是 width * height * bytesPerPixel