xcode 如何使用SwiftUI在macOS上将Uint8数组显示为图像

kh212irz  于 2023-06-24  发布在  Swift
关注(0)|答案(1)|浏览(155)

我刚刚开始学习Xcode、SwiftUI等。通过创建一个学习应用程序,这也是有用的。我能够将数据导入为[UInt 16]并将其缩放为[UInt 8],以显示为灰度图像。我创建了一个CGImage,它被分配给一个状态变量,但我得到了这个错误:
2023-06-13 10:03:04.497662-0700 DisplayFM 2 Image [6160:76497] [default] CGSWindowShmemCreateWithPort在端口0失败
我在CGImage创建后有一个断点;当我在调试会话中检查图像时,图像显示!看起来我正在做正确的事情,但是当我在调试中“继续”时,错误出现了。我一直无法弄清楚错误是在哪里产生的,但认为它是在显示/视图中。我将缩放后的[UInt 8]和图像元数据保存在类TheImage的示例theImage中。
我怀疑cgImage在函数“createCGImageFromUint 8“返回后没有保留指向图像数据的指针(或类似的),或者GeometryReader中的代码不正确。
其目的是允许用户拾取包含图像数据的文件,显示数据,并让用户在工具提示处检查未缩放的数据,然后使用另一个数据集重复。
(It具有讽刺意味的是,该应用程序的早期版本可以工作,但我没有使用最佳实践-加载和缩放图像的ContentView代码可能会在每次ContentView更新时调用。)

import SwiftUI
import CoreGraphics

// Calculates a Core Graphics image as ContentView's
// State variable "cgImage".
func createCGImageFromUint8( _ cgImage: inout CGImage?) {
    
    let width: Int = theImage.nCols
    let height: Int = theImage.nRows
    let numBytes = theImage.scaledArray.count
    let numComponents = numBytes / (height * width)
    let colorspace = CGColorSpaceCreateDeviceGray()
    let grayScaleData = CFDataCreate(nil, theImage.scaledArray, numBytes)
    let provider = CGDataProvider(data: grayScaleData!)!
    
    cgImage = CGImage(
        width: width,
        height: height,
        bitsPerComponent: 8,
        bitsPerPixel: 8 * numComponents,
        bytesPerRow: width * numComponents,
        space: colorspace,
        //bitmapInfo: CGBitmapInfo(rawValue: 0),
        bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue),
        provider: provider,
        decode: nil,
        shouldInterpolate: true,
        intent: CGColorRenderingIntent.defaultIntent)!
    
    // break point
     print("Return from createCGImageFromUint8")
    // Looking at cgImage in debug at this breakpoint
    // displays the image!
}

这是我的ContentView的开始:

import SwiftUI
import CoreGraphics

var theImage = TheImage() // Image vars, file

struct ContentView: View {
    
    @State var haveNewImage: Bool = false
    @State private var cgImage: CGImage? = nil
        
    // @State var imageDataView: (any View)? // what's this?
    
    var body: some View {
        VStack(alignment: .leading ) {
            
            FilePickerView(cgImage: $cgImage)
                .padding()  // at upper left corner WORKS
            
            // If a new file was picked: process and display it.
            if theImage.isNew { // displays image etc. GOAT
                Text("DEBUG New Image")
                CGImageView(cgImage: $cgImage)
            } else {
                HStack {
                    Spacer()
                    Text("Image not yet selected.")
                    Spacer()
                }
            }
        }    
    }

这是我的CGImageView。

import SwiftUI
import CoreGraphics
import AppKit

struct CGImageView: View {
    @Binding var cgImage: CGImage?

    @State private var opacity = 0.0
    @State private var minMax = theImage.minMax
    @State private var dnData: [UInt16] = theImage.backingArray
    
    var body: some View {
        var dnText = "" // to display tooltip
        Text(theImage.displayName)
            .padding(10)
        
        HStack {
            GeometryReader { geometry in
            //    Image(decorative: $cgImage as! CGImage , scale: 1.0)
                  Image(decorative: self.cgImage! , scale: 1.0)
                    .aspectRatio(contentMode: .fit)
                    .border(Color.red)
            }
            .frame(width: CGFloat(theImage.nCols), height: CGFloat(theImage.nRows))
            .opacity(opacity)
            .animation(.easeInOut(duration: 1.5), value: opacity)
            
            .onAppear {
                opacity = 1.0
            }
            
            .onDisappear {
                opacity = 0.0
            }
            
            .onTapGesture {
                print("TAPPED!") // do something
            }
            .onHover { (hover: Bool) in
                if hover {
                    //   let mouseLocation = NSEvent.mouseLocation
                    //   print("Mouse location: \(mouseLocation)")
                    NSCursor.crosshair.push()
                } else {
                    NSCursor.pop()
                }
            }
            
            .onContinuousHover { phase in
                switch phase {
                case .active(let location):  // col, row
                    let dn = theImage.getToolTipValue(mouse: location)
                    dnText = "raw DN \(dn)"
                    print("---> location x: \(location.x)")
                case .ended:
                    break
                }
            }
            .padding(10)

            Text("Mimimum \(minMax[0])\nMaximum \(minMax[1])\n\(dnText)")
                
        }
    }
}
snz8szmq

snz8szmq1#

在创建一个工作的最小示例时,我发现CGImage是不可变的,我有效地改变了它。我通过将CGImage与生成CGImage的代码沿着存储在我的TheImage类中来修复这个问题。
ContentView:

import SwiftUI

var theImage = TheImage()

struct ContentView: View {
    
    //@State var cgImage: CGImage? = nil
    @State var haveImage: Bool = false
    
    var body: some View {

        VStack {
            
            Button(action: {
                theImage.makeScaledArray()
                theImage.createCGImageFromUint8()
                haveImage = true
            }) {
                Text("Click here!")
                    .fontWeight(.regular)
                    .font(.title2)
                    .foregroundColor(.green)
            }
            
            if(haveImage) {
                CGImageView()
            } else {
                Text("Image will display here.")
            }
        }
        .padding()
    }
}

这个类保存缩放后的数组(并将保存原始数据)和对数据进行操作的函数。

import Foundation
import CoreGraphics

class TheImage {
    
    init() {
    }
    
    var nCols: Int = 320
    var nRows: Int = 240
    var scaledArray: [UInt8]?
    var cgImage: CGImage?
    
    func makeScaledArray() {
        let size: Int = self.nCols * self.nRows
        var array: [UInt8] = Array(repeating: 0, count: size)
        for i in 0..<size {
            array[i] = UInt8(i % 256)
        }
        self.scaledArray = array
    }
    
    func createCGImageFromUint8() {
        
        let width = self.nCols
        let height = self.nRows
        let numBytes = self.scaledArray!.count
        let numComponents = numBytes / (height * width)
        let colorspace = CGColorSpaceCreateDeviceGray()
        let grayScaleData = CFDataCreate(nil, self.scaledArray, numBytes)
        let provider = CGDataProvider(data: grayScaleData!)!
        
        self.cgImage = CGImage(
            width: width,
            height: height,
            bitsPerComponent: 8,
            bitsPerPixel: 8 * numComponents,
            bytesPerRow: width * numComponents,
            space: colorspace,
            bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue),
            provider: provider,
            decode: nil,
            shouldInterpolate: true,
            intent: CGColorRenderingIntent.defaultIntent)
        
        print("DEBUG cgImage created.")
    }

}

最后,ImageView包括一点显示糖。

import SwiftUI
import CoreGraphics
import AppKit

struct CGImageView: View {
    @State private var opacity = 0.0
    
    var body: some View {
        
        HStack {
            GeometryReader { geometry in
                //    Image(decorative: $cgImage as! CGImage , scale: 1.0)
                Image(decorative: theImage.cgImage! , scale: 1.0)
                    .aspectRatio(contentMode: .fit)
                    .border(Color.red)
            }
            .frame(width: CGFloat(theImage.nCols), height: CGFloat(theImage.nRows))
            .opacity(opacity)
            .animation(.easeInOut(duration: 1.5), value: opacity)
            
            .onAppear {
                opacity = 1.0
            }
            
            .onDisappear {
                opacity = 0.0
            }
            
            
        }
        .padding(10)
        
        }
    
}

相关问题