在 Swift 中将 RGB 像素数据数组转换为 CGImage

问题描述 投票:0回答:1

我真的非常非常接近这里,但我只是不知道为什么会出错。看起来 NSData 或 CGDataProvider 或 CGImage 正在跳过每 8 行,然后用垃圾填充剩余的画布,所以我只得到图像的 1/8 聚集在顶部。

pixeldata.png rendered

我认为这一定是一些数学错误,但在多次检查代码后,我不确定这可能在哪里。

我正在尝试在 MacOS 上学习一些 Swift,几周后我已经成功做到这一点并开始做一些有趣的事情。如果有人可以帮助我,我将非常感激!

此外,最好知道我是否是有效绘制单个像素的正确途径。人们似乎并不难找到这方面的好例子。

import Foundation
import Cocoa
import UniformTypeIdentifiers

extension CGImage
{
    //https://stackoverflow.com/questions/1320988/saving-cgimageref-to-a-png-file
    var png: Data?
    {
        let cfdata: CFMutableData = CFDataCreateMutable(nil, 0)
        if let destination = CGImageDestinationCreateWithData(cfdata, String(describing: UTType.png) as CFString, 1, nil)
        {
            CGImageDestinationAddImage(destination, self, nil)
            if CGImageDestinationFinalize(destination)
            {
                return cfdata as Data
            }
        }
        return nil
    }
}

//https://blog.human-friendly.com/drawing-images-from-pixel-data-in-swift
struct PixelData {
    var a:UInt8 = 255
    var r:UInt8
    var g:UInt8
    var b:UInt8
}

func pixeldata_to_image(pixels: [PixelData], width: Int, height: Int)->CGImage
{
    assert(pixels.count == Int(width * height))
    let bitsPerComponent: Int = 8
    let bitsPerPixel: Int = 32
    let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
    let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue)

    var data = pixels
    guard
        let providerRef = CGDataProvider(data: NSData(bytes: &data, length: data.count * bitsPerPixel))
    else
    {
        fatalError("CGDataProvider failure")
    }
    guard
        let image = CGImage(
            width: width,
            height: height,
            bitsPerComponent:bitsPerComponent,
            bitsPerPixel: bitsPerPixel,
            bytesPerRow: width * bitsPerPixel,
            space: rgbColorSpace,
            bitmapInfo: bitmapInfo,
            provider: providerRef,
            decode: nil,
            shouldInterpolate: true,
            intent: .defaultIntent
        )
    else
    {
        fatalError("CGImage failure")
    }
    return image
}

func testUsage()
{
    let range_x: Int = 200
    let range_y: Int = 200
    let greyPixel = PixelData(a: 255, r: 128, g: 128, b: 128)
    let blackPixel = PixelData(a: 255, r: 0, g: 0, b: 0)
    let whitePixel = PixelData(a: 255, r: 255, g: 255, b: 255)
    let yellowPixel = PixelData(a: 255, r: 255, g: 255, b: 0)
    let greenPixel = PixelData(a: 255, r: 0, g: 255, b: 0)
    let redPixel = PixelData(a: 255, r: 255, g: 0, b: 0)
    var pixelData = [PixelData](repeating: greyPixel, count: Int(range_x * range_y))
    var index: Int = 0
    //populate pixelData
    for x in 0 ..< range_x
    {
        for y in 0 ..< range_y
        {
            let pixel: PixelData
            if x == 0 && y == 0 || x == 0 && y == range_x - 1 || x == 8 && y == 8
            {
                //grey draw specfic pixels
                pixel = greyPixel
            }
            else if x == y
            {
                //yellow draw diagonal from top-left to bottom-right
                pixel = yellowPixel
            }
            else if y == 0 || x == 0
            {
                //dblack raw top line and draw left line
                pixel = blackPixel
            }
            else if y == range_y - 1 || x == range_y - 1
            {
                //white draw bottom line and draw right line
                pixel = whitePixel
            }
            else if y == range_y / 2 || x == range_x / 2
            {
                //green draw horizontal center line and draw vertical center line
                pixel = greenPixel
            }
            else
            {
                pixel = redPixel
            }
            //print("x:", x, "y:", y, "index:", index, "pixel:", pixel) //sanity check
            pixelData[index] = pixel
            index += 1
        }
    }
    let image: CGImage = pixeldata_to_image(pixels: pixelData, width: range_x, height: range_y)
    try? image.png!.write(to: URL(fileURLWithPath: "pixeldata.png"))
}

swift macos cocoa nsdata cgimage
1个回答
0
投票

正如评论中指出的,我混淆了一些位和字节。我添加了这两行:

    let bitsPerByte = 8
    let bytesPerPixel: Int = bitsPerPixel/bitsPerByte

然后修改这两行:

        -let providerRef = CGDataProvider(data: NSData(bytes: &data, length: data.count * bitsPerPixel))
        +let providerRef = CGDataProvider(data: NSData(bytes: &data, length: data.count * bytesPerPixel))

            -bytesPerRow: width * bitsPerPixel,
            +bytesPerRow: width * bytesPerPixel,

现在就像魅力一样!

© www.soinside.com 2019 - 2024. All rights reserved.