在Core Graphics中连续滚动动态位图

时间:2016-02-19 21:12:47

标签: swift core-graphics core-animation

我一直在努力解决基于Cocoa的简单可视化程序的性能相关问题,该程序不断将热图绘制为位图并在屏幕上显示位图。从广义上讲,热图是不断滚动的实时信息源。在每次重绘时,在位图的右侧添加新像素,然后将位图向左移动,从而创建持续的信息流。

我当前的方法是在自定义NVSView中实现的,具有用于底层像素的循环缓冲区。实际上,图像被旋转90度,因此添加新数据只需要附加缓冲区(向图像添加行)。然后我使用CGDataProviderCreateWithData创建CoreGraphics图像并将其绘制到旋转的上下文中。 (代码如下,虽然对问题不太重要。)

我希望找到一种更有效的方法来实现这一目标。看起来每次重绘完整位图都是过度的,在我的尝试中,它使用了惊人的高CPU(~20-30%)。我觉得应该有办法以某种方式指示GPU缓存,但是移动现有像素然后追加新数据。

我正在考虑一种方法,它将使用两个CGBitmapContextCreate,一个上下文用于当前屏幕上的内容,另一个上下文用于修改。先前的像素将从一个上下文复制到另一个上下文,但我不确定这会显着提高性能。在我走得太远之前,有没有更好的方法来处理这种更新?

以下是我当前实施的相关代码,但我认为我更需要更高级别的指导:

class PlotView: NSView
{
    private let bytesPerPixel = 4
    private let height = 512
    private let bufferLength = 5242880
    private var buffer: TPCircularBuffer

    // ... Other code that appends data to buffer and set needsDisplay ...

    override func drawRect(dirtyRect: NSRect) {
        super.drawRect(dirtyRect)

        // get context
        guard let context = NSGraphicsContext.currentContext() else {
            return
        }

        // get colorspace
        guard let colorSpace = CGColorSpaceCreateDeviceRGB() else {
            return
        }

        // number of sampels to draw
        let maxSamples = Int(frame.width) * height
        let maxBytes = maxSamples * bytesPerPixel

        // bytes for reading
        var availableBytes: Int32 = 0
        let bufferStart = UnsafeMutablePointer<UInt8>(TPCircularBufferTail(&buffer, &availableBytes))
        let samplesStart: UnsafeMutablePointer<UInt8>
        let samplesCount: Int
        let samplesBytes: Int

        // buffer management
        if Int(availableBytes) > maxBytes {
            // advance buffer start
            let bufferOffset = Int(availableBytes) - maxBytes

            // set sample start
            samplesStart = bufferStart.advancedBy(bufferOffset)

            // consume values
            TPCircularBufferConsume(&buffer, Int32(bufferOffset))

            // number of samples
            samplesCount = maxSamples
            samplesBytes = maxBytes
        }
        else {
            // set to start
            samplesStart = bufferStart

            // number of samples
            samplesBytes = Int(availableBytes)
            samplesCount = samplesBytes / bytesPerPixel
        }

        // get dimensions
        let rows = height
        let columns = samplesCount / rows

        // bitmap info
        let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.NoneSkipFirst.rawValue)

        // counts
        let bytesPerComponent = sizeof(UInt8)
        let bytesPerPixel = sizeof(UInt8) * bytesPerPixel

        // prepare image
        let provider = CGDataProviderCreateWithData(nil, samplesStart, samplesBytes, nil)
        let image = CGImageCreate(rows, columns, 8 * bytesPerComponent, 8 * bytesPerPixel, rows * bytesPerPixel, colorSpace, bitmapInfo, provider, nil, false, .RenderingIntentDefault)

        // make rotated size
        let rotatedSize = CGSize(width: frame.width, height: frame.height)
        let rotatedCenterX = rotatedSize.width / 2, rotatedCenterY = rotatedSize.height / 2

        // rotate, from: http://stackoverflow.com/questions/10544887/rotating-a-cgimage
        CGContextTranslateCTM(context.CGContext, rotatedCenterX, rotatedCenterY)
        CGContextRotateCTM(context.CGContext, CGFloat(0 - M_PI_2))
        CGContextScaleCTM(context.CGContext, 1, -1)
        CGContextTranslateCTM(context.CGContext, -rotatedCenterY, -rotatedCenterX)

        // draw
        CGContextDrawImage(context.CGContext, NSRect(origin: NSPoint(x: rotatedSize.height - CGFloat(rows), y: 0), size: NSSize(width: rows, height: columns)), image)
    }
}

0 个答案:

没有答案