如何将位图图像保存到png和/或pdf文件

时间:2017-05-01 21:42:32

标签: swift graphics png bitmapimage

我试图弄清楚如何在mac OS上使用Swift和Cocoa创建位图 我在网上找到了这个例子。

// LibPNG example
// A.Greensted
// http://www.labbookpages.co.uk

使用libpng和桥接头我将此示例转换为Swift。我不想通常使用libpng,因为它似乎不是Apple提供的标准框架之一。我正在尝试学习如何使用Core Graphics和Cocoa。 (我试图避免使用目标C并使用Swift)

我使用上面在操场上运行的示例编写了以下代码。在边缘我得到一个很好的mandelbrot图像。我无法弄清楚如何将其转换为pdf和/或png以及如何将其保存到文件中。看起来这应该很容易。关于如何做到这一点的指针将不胜感激;或指向先前答案的指针。 Apple经常更改他们的API,许多答案都是陈旧的。

如果您运行我的示例,如果您在操场上运行此函数,则将函数(createMandelbrot,setRGB,makeRGBImage)放在sources目录中的单独文件中。如果mandelbrot函数位于主操场窗口中,则迭代将永远持续。

import Cocoa
let h = 500
let w = 500
let mandelbrot = createMandelbrot(width: w, height: h, xS:  -0.802, yS: -0.177, rad: 0.011, maxIteration: 110)
let context = makeRGBImage(width: w, height: h, buffer: mandelbrot)
let image = NSImage(cgImage: context.makeImage()!, size: NSSize(width: w, height: h))

下面是“源目录”中的文件。

import Foundation
public func createMandelbrot(width: Int, height: Int, xS: Double, yS:  Double, rad: Double, maxIteration:  Int) -> [Double] {
var buffer = Array<Double>(repeating: 0.0, count: width * height)
var minMu = Double(maxIteration)
var maxMu = 0.0
for yPos in 0..<height {
    let yP: Double = (yS-rad) + (2.0 * rad/Double(height)) * Double(yPos);

    for xPos in 0..<width {
        let xP: Double = (xS - rad) + (2.0 * rad/Double(width)) * Double(xPos)

        var iteration:Int = 0
        var x = 0.0
        var y = 0.0
        while x * x + y * y <= 4 && iteration < maxIteration {
            let tmp = x * x - y * y + xP
            y = 2 * x * y + yP
            x = tmp;
            iteration += 1
        }
        if iteration < maxIteration {
            let modZ = sqrt(x * x + y * y)
            let mu = Double(iteration) - (log(log(modZ))) / log(2.0)
            if mu > maxMu {
                maxMu = mu
            }
            if mu < minMu {
                minMu = mu
            }
            buffer[yPos * width + xPos] = mu
        }
        else {
            buffer[yPos * width + xPos] = 0
        }
    }
}
// Scale buffer values between 0 and 1
var count = width * height;
while count > 0 {
    count -= 1
    buffer[count] = (buffer[count] - minMu) / (maxMu - minMu)
}
return buffer
}



import Cocoa
func setRGB(ptr: inout [UInt8], val: Double)
{
var v = Int(val * 767)
if (v < 0) {
    v = 0
} else if (v > 767){
    v = 767
}
let offset: Int = v % 256

if (v<256) {
    ptr[0] = 0; ptr[1] = 0; ptr[2] = UInt8(offset)
}
else if (v<512) {
    ptr[0] = 0; ptr[1] = UInt8(offset); ptr[2] = 255 - UInt8(offset)
}
else {
    ptr[0] = UInt8(offset); ptr[1] = 255 - UInt8(offset); ptr[2] = 0
}
ptr[3] = 255
}


public func makeRGBImage(width: Int, height: Int, buffer: [Double]) -> CGContext
{
let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue
let colorSpace = CGColorSpaceCreateDeviceRGB()
let context = CGContext(data: nil, width: width, height: height, bitsPerComponent: 8, bytesPerRow: 4 * width, space: colorSpace, bitmapInfo: bitmapInfo)
var pix: [UInt8] = [0,0,0,0]
let dta = UnsafeMutablePointer<UInt8>(mutating: context?.data?.assumingMemoryBound(to: UInt8.self))!
// Write image data
for y in 0..<height {
    let jmp = 4 * width * y
    for x in 0..<width {
        let indxBuf = y * width + x
        setRGB(ptr: &pix, val: buffer[indxBuf])
        let indxPix = 4 * x + jmp
        dta[indxPix] = pix[0]   // red
        dta[indxPix+1] = pix[1] // green
        dta[indxPix+2] = pix[2] // blue
        dta[indxPix+3] = pix[3] // alpha
    }
}
return context!
}

2 个答案:

答案 0 :(得分:1)

您的代码开始绘制图形上下文。下一步可能是从上下文(CGBitmapContextCreateImage)获取CGImage。从那里,您可以创建一个数据使用者,将信息放入文件(CGDataConsumer(url: ___)),然后创建类型为CGImageDestinationCreateWithDataConsumer的图像目标(kUTTypePNG)并将图像添加到目标(CGImageDestinationAddImage

请务必致电CGImageDestinationFinalize

答案 1 :(得分:0)

根据Scott Thompson的建议,我在主操场页面写了以下内容。它会产生一个打开的png文件,但只是一个没有mandelbrot的灰色框。 PDF无法生成可以打开的文件。

import Cocoa
let h = 500
let w = 500
let mandelbrot = createMandelbrot(width: w, height: h, xS:  -0.802, yS: -0.177, rad: 0.011, maxIteration: 110)
let context = makeRGBImage(width: w, height: h, buffer: mandelbrot)
let url = URL(fileURLWithPath: "/Users/judd/local/mandelbrot.png")
let dc = CGDataConsumer(url: url as CFURL)
let dest = CGImageDestinationCreateWithDataConsumer(dc!, kUTTypePNG, 1, nil)
CGImageDestinationAddImage(dest!, context.makeImage()!, nil)
CGImageDestinationFinalize(dest!)

然后我使用完全相同的代码,除了操场,我开始了一个新项目并将操场代码放在main中。一切都很好。然后我回到了游乐场环境并在Sources中创建了一个新文件,我在其中放置了一个包含以下函数的文件。

import Cocoa
public func writeToPNG(context: CGContext) {
   let url = URL(fileURLWithPath: "/Users/judd/local/mandelbrot.png")
    let dc = CGDataConsumer(url: url as CFURL)
    let dest = CGImageDestinationCreateWithDataConsumer(dc!, kUTTypePNG, 1, nil)
    CGImageDestinationAddImage(dest!, context.makeImage()!, nil)
    CGImageDestinationFinalize(dest!)
}
public func writeToPDF(context: CGContext) {
    let url = URL(fileURLWithPath: "/Users/judd/local/mandelbrot.pdf")
    let dc = CGDataConsumer(url: url as CFURL)
    let dest = CGImageDestinationCreateWithDataConsumer(dc!, kUTTypePDF, 1, nil)
    CGImageDestinationAddImage(dest!, context.makeImage()!, nil)
    CGImageDestinationFinalize(dest!)
}

我的主要游乐场页面现在看起来像

import Cocoa
let h = 500
let w = 500
let mandelbrot = createMandelbrot(width: w, height: h, xS:  -0.802, yS: -0.177, rad: 0.011, maxIteration: 110)
let context = makeRGBImage(width: w, height: h, buffer: mandelbrot)
writeToPNG(context: context)
writeToPDF(context: context)

这很好用,我在我的文件中得到了一个合适的png和pdf图像。从主操场页面做事情似乎存在问题,导致一些混乱。