【问题标题】:My pixel map image (NSImage) sometimes doesn't look like the RGB values I pass in, Swift Core Graphics Quartz我的像素图图像 (NSImage) 有时看起来不像我传入的 RGB 值,Swift Core Graphics Quartz
【发布时间】:2015-11-26 00:42:25
【问题描述】:

我有两个 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 实例的 image 属性中...

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 调用时总是正确的。特别是对于以下示例,它们是 64 个 Double 值(从 0.0 到 63.0),它们将使用我构建的彩条映射到 64 个 UInt32 值,以便第一个像素是纯红色( 0xff0000ff),最后一个像素是纯白色(0xffffffff)。在任何值中,我们都看不到任何转化为黑色的东西。当排列为 64 x 一张地图(本质上是颜色条)时,它应该看起来像这样......

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

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

【问题讨论】:

    标签: swift core-graphics quartz-2d


    【解决方案1】:

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

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

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

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

    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 环境中不做的事情。但这取决于你。

    【讨论】:

    • 感谢您的清晰解释。我没有看到“超出范围”-> 释放和释放连接。我认为它一定与内存释放/重用有关,但考虑到我对 Swift 参考的使用方式,我看不出它是如何成为 mine 的计数器系统。我现在正在尝试您的第二种技术的变体,但我很好奇 let x = width/4 行的用途是什么。
    • 抱歉,这是一些不相关的代码,我用它把渐变分成四个不同的区域来重现你的图像。但这与手头的问题无关,因此我已将其删除。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-02-15
    • 2015-03-07
    • 1970-01-01
    • 2015-02-13
    • 2012-02-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多