2015-11-26 5 views
1

У меня есть две карты d-пикселей ([Double]), которые я кодирую (правильно) в [UInt32]). , который подается в CGDataProvider, который является источником для CGImage, преобразуется в NSImage и, наконец, отображается в пользовательском представлении сМое изображение в пиксельной карте (NSImage) иногда не похоже на значения RGB, которые я передаю, Swift Core Graphics Quartz

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 
} 

Я проверил, что созданные значения буфера всегда c orrect при подаче в вызов CGDataProviderCreateWithData. В частности, для следующих примеров, которые представляют собой шестьдесят четыре значения Double (от 0,0 до 63,0), которые будут отображаться в шестьдесят четыре значения UInt32, используя цветную полосу, которую я построил так, что первый пиксель является чисто красным (0xff0000ff) и последний пиксель чистый белый (0xffffffff). Ни в одном из значений мы не видим ничего, что переводится в черный. Когда одевался как 64 на одной карте (это, по существу, цветной панели) он должен выглядеть следующим образом ...

64x1 correct image

, но иногда, просто reinvokeing метод DrawRect, с идентичными данными и созданный буфер это выглядит совершенно неправильно

64x1 incorrect image

или иногда почти вправо (один пиксель неправильный цвет). Я бы опубликовал больше примеров, но ограничился только двумя ссылками.

ответ

2

Проблема в том, что вы создали поставщика данных с небезопасным указателем на buffer, заполнили этот буфер пикселей, создали изображение с помощью этого провайдера, но затем разрешено buffer выходить за рамки и выпускаться, даже несмотря на то, что у поставщика данных по-прежнему была небезопасная ссылка на этот адрес памяти.

Но как только эта память была освобождена, ее можно использовать для других вещей, оказывая странное поведение в пределах от одного пикселя, который выключен, или оптового изменения диапазона памяти, поскольку он используется для других вещей.

В среде Objective-C, я сделал бы malloc из памяти, когда я создал поставщик, а затем последний параметр CGDataProviderCreateWithData будет функцией C, что я бы написать free, что память. (А что функция памяти освобождения не может быть названа намного позже, не до изображения было освобождено.)

Другим подхода, который я использовал, это вызвать 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) 
} 

Это дает:

enter image description here

Можно сделать аргумент о том, что первый метод (создать данные провайдер, сделав malloc из памяти, а затем предоставив CGDataProviderCreateWithData параметр функции, чтобы освободить его), может быть лучше, но тогда вы застряли над написанием функции C для этой очистки, чего я бы предпочел не делать в среде Swift. Но это зависит от вас.

+0

Благодарим вас за четкое объяснение. Я не видел «выход из сферы действия» -> освобождение и освобождение соединения. Я думаю, что это должно иметь какое-то отношение к освобождению памяти/повторному использованию, но не могло понять, как это было из-за того, как я использовал систему счетчиков Swift. Сейчас я пытаюсь использовать вариант вашей второй техники, но мне любопытно, что использование строки 'let x =' width/4 было. –

+0

К сожалению, это был немного несвязанный код, который я использовал для разделения градиента на четыре разных зоны для воспроизведения вашего изображения. Но это не связано с проблемой, поэтому я удалил ее. – Rob