我的像素图图像 (NSImage) 有时看起来不像我传入的 RGB 值,Swift Core Graphics Quartz

My pixel map image (NSImage) sometimes doesn't look like the RGB values I pass in, Swift Core Graphics Quartz

我有两个 d 像素图 ([Double]),我将其(正确地)编码为 [UInt32]),它被馈送到 CGDataProvider,它是 CGImage 的来源,转换为 NSImage,最后在自定义视图中显示

class SingleStepMapView:NSView {
@IBOutlet var dataSource : SingleStepViewController!

override func drawRect(dirtyRect: NSRect) {
    let mapImage = TwoDPixMap(data: dataSource.mapData, 
    width: dataSource.mapWidth, color: dataSource.mapColor).image

    mapImage.drawInRect(self.bounds, fromRect: NSZeroRect, operation: NSCompositingOperation.CompositeSourceAtop, fraction: 1.0)
    return 
}

构造NSImage的部分在TwoDPixMap实例的图像属性中...

var image : NSImage {
    var buffer = [UInt32]()
    let bufferScale = 1.0 / (mapMax - mapMin)
    for d in data {
        let scaled = bufferScale * (d - mapMin)
        buffer.append( color.rgba(scaled) )
    }
    let bufferLength = 4 * height * width
    let dataProvider = CGDataProviderCreateWithData(nil, buffer, bufferLength, nil)!
    let bitsPerComponent = 8
    let bitsPerPixel = 32
    let bytesPerRow = 4 * width
    let colorSpace = CGColorSpaceCreateDeviceRGB()
    var bitMapInfo = CGBitmapInfo()
    bitMapInfo.insert(.ByteOrderDefault)
    //        let bitMapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.PremultipliedFirst.rawValue)
    let interpolate = false
    let renderingIntent = CGColorRenderingIntent.RenderingIntentDefault
    let theImage = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpace, bitMapInfo, dataProvider, nil, interpolate, renderingIntent)!
    let value = NSImage(CGImage: theImage, size: NSSize(width: width, height: height))
    return value
}

我已经检查过当输入 CGDataProviderCreateWithData 调用时创建的缓冲区值总是正确的。特别是对于以下示例,它们是六十四个 Double 值(从 0.0 到 63.0),它们将使用我构建的颜色条映射到六十四个 UInt32 值,这样第一个像素是纯红色 (0xff0000ff),最后一个像素是纯白色 (0xffffffff)。在 none 的值中,我们看到任何转化为黑色的东西。当排列成一张 64 乘一张地图(它本质上是颜色条)时,它应该看起来像这样...

但有时,只是重新调用 drawrect 方法,使用相同的数据和创建的缓冲区,它看起来完全错误

或有时 几乎 正确(一个像素颜色错误)。我会 post 更多示例,但仅限于两个链接

问题是您创建了一个带有指向 buffer 的不安全指针的数据提供程序,填充了该像素缓冲区,使用该提供程序创建了图像,但随后允许 buffer 脱离范围并被释放,即使数据提供者仍然对该内存地址有不安全的引用。

但是一旦该内存被释放,它就可以用于其他事情,呈现奇怪的行为,从关闭的单个像素,或者在用于其他事情时对该内存范围进行大规模更改。

在 Objective-C 环境中,我会在创建提供程序时对内存执行 malloc,然后 CGDataProviderCreateWithData 的最后一个参数将是一个 C 函数,我会写入 free 那个记忆。 (而且释放内存的函数可能要等到很久以后才会被调用,直到图像被释放。)

我使用的另一种方法是调用 CGBitmapContextCreate 来创建上下文,然后使用 CGBitmapContextGetData 来获取它为图像创建的缓冲区。然后我可以按照我认为合适的方式填充该缓冲区。但是因为那个缓冲区是为我创建的,所以 OS 负责内存管理:

func createImageWithSize(size: NSSize) -> NSImage {
    let width = Int(size.width)
    let height = Int(size.height)

    let bitsPerComponent = 8
    let bytesPerRow = 4 * width
    let colorSpace = CGColorSpaceCreateDeviceRGB()
    let bitmapInfo = CGImageAlphaInfo.PremultipliedLast.rawValue   // this depends upon how your `rgba` method was written; use whatever `bitmapInfo` that makes sense for your app

    let context = CGBitmapContextCreate(nil, width, height, bitsPerComponent, bytesPerRow, colorSpace, bitmapInfo)!

    let pixelBuffer = UnsafeMutablePointer<UInt32>(CGBitmapContextGetData(context))

    var currentPixel = pixelBuffer

    for row in 0 ..< height {
        for column in 0 ..< width {
            let red = ...
            let green = ...
            let blue = ...

            currentPixel.memory = rgba(red: red, green: green, blue: blue, alpha: 255)
            currentPixel++
        }
    }

    let cgImage = CGBitmapContextCreateImage(context)!

    return NSImage(CGImage: cgImage, size: size)
}

结果:

第一种技术(创建数据提供程序,执行 malloc 内存然后提供 CGDataProviderCreateWithData 函数参数以释放它)可能更好,但是然后你就不得不编写一个 C 函数来进行清理,这是我不想在 Swift 环境中做的事情。但这取决于你。