SwiftUI - 从图像中获取像素颜色

3
我有一个 SwiftUI 图像,显示方式如下:
Image("Map")
     .resizable()
     .gesture(
        DragGesture(minimumDistance: 0, coordinateSpace: .global)
          .onChanged { value in
              self.position = value.location
              print(position)
              let color = getColor(at: position)
              print(color)
           }
           .onEnded { _ in
              self.position = .zero
           }
        )

还有一个getColor函数:

func getColor(at point: CGPoint) -> UIColor{

        let pixel = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: 4)
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)
        let context = CGContext(data: pixel, width: 1, height: 1, bitsPerComponent: 8, bytesPerRow: 4, space: colorSpace, bitmapInfo: bitmapInfo.rawValue)!

        context.translateBy(x: -point.x, y: -point.y)
        let color = UIColor(red:   CGFloat(pixel[0]) / 255.0,
                            green: CGFloat(pixel[1]) / 255.0,
                            blue:  CGFloat(pixel[2]) / 255.0,
                            alpha: CGFloat(pixel[3]) / 255.0)

        pixel.deallocate()
        return color
    }

当我启动应用程序并点击图像时,位置在控制台上正确显示,但颜色返回为0 0 0 0,例如:
(262.3333282470703, 691.6666564941406)
UIExtendedSRGBColorSpace 0 0 0 0

在长时间的搜索后,我没有找到一种在SwiftUI中从图像中获取像素颜色的方法。如果有人有解决方案或可以帮助我,那真的会很不错。
提前致谢。

https://dev59.com/IF8e5IYBdhLWcg3w_-lN - undefined
嗨,感谢你的回答,但这个解决方案是针对UIKit而不是SwiftUI,对吗? - undefined
SwiftUI不提供CoreGraphics API上下文。由于您正在尝试通过CGContext(UIKit方式)获取值,这可能是您遇到问题的根本原因。解决您的问题的另一种方法可能是使用CoreImage API,可以在这里找到一个示例。 - undefined
https://stackoverflow.com/users/4930934/burak-akkaş - 现在可以通过ImageRenderer实现。 - undefined
1个回答

1
这可能是一个比必要更复杂的解决方案,但它有效。
基本上,它使用ImageRenderer创建了一个基于内存的Image副本,同时通过GeometryReader捕获了可见Image的实际尺寸。尺寸差异用于缩放拖动位置。
然后可以查询基于内存的视图的cgImage属性,并在缩放的拖动坐标处选择出CGImage像素。
以下是代码示例 - 它显示了一张图片(我将其裁剪为圆形,因为它是一个色轮,但这不是必要的),叠加了一个光标,显示了光标坐标和光标下的Color。
//
//  PixelPickerView.swift
//  PixelPicker
//
//  Created by Grimxn on 18/11/2023.
//

import SwiftUI

struct PixelPickerView: View {
    @State private var cursor = CGPoint.zero.  // Drag position
    @State private var colourSelected: Color?  // Pixel Color
    @State private var x = 0                   // Scaled drag x
    @State private var y = 0                   // Scaled drag y
    @State private var myImage = Image("ColourWheel")

    var body: some View {
        VStack {
            GeometryReader { g in
                let diameter: CGFloat = min(g.size.width, g.size.height)
                HStack {
                    Spacer()
                    myImage
                        .resizable()
                        .scaledToFit()
                        .overlay {
                            cursor(diameter: diameter)
                        }
                        .clipShape(Circle())
                        .gesture(
                            DragGesture(minimumDistance: 0)
                                .onChanged {
                                    cursor = $0.location
                                }
                                .onEnded {_ in
                                    colourAtOffset(diameter: diameter)
                                }
                        )
                    Spacer()
                }
            }
            Text("\(x), \(y)")
            Rectangle()
                .foregroundStyle(colourSelected ?? .clear)
            Spacer()
        }
    }
    
    private func colourAtOffset(diameter: CGFloat) {
        Task {
            if let cgi = await ImageRenderer(content: myImage).cgImage {
                if let dp = cgi.dataProvider,
                   let pixelData = dp.data {
                    let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)

                    x = Int(CGFloat(cgi.width) * cursor.x / diameter)
                    y = Int(CGFloat(cgi.height) * cursor.y / diameter)
                    let pixelInfo: Int = (cgi.bytesPerRow * y) + x * 4
                    if cgi.bitmapInfo.contains(.byteOrderDefault) {
                        let r = CGFloat(data[pixelInfo + 0]) / CGFloat(255)
                        let g = CGFloat(data[pixelInfo + 1]) / CGFloat(255)
                        let b = CGFloat(data[pixelInfo + 2]) / CGFloat(255)
                        let a = CGFloat(data[pixelInfo + 3]) / CGFloat(255)
                        colourSelected = Color(red: r, green: g, blue: b, opacity: a)
                    } else {
                        colourSelected = .brown // Brown for debug, should be `nil`
                    }
                }
            }
        }
    }
    
    private func cursor(diameter: CGFloat) -> some View {
        let r = diameter / 2
        let wee = CGFloat(5)
        let p = Path { p in
            p.move(to: CGPoint(x: 0, y: r))
            p.addLine(to: CGPoint(x: r - wee, y: r))
            p.move(to: CGPoint(x: r + wee, y: r))
            p.addLine(to: CGPoint(x: 2 * r, y: r))
            
            p.move(to: CGPoint(x: r, y: 0))
            p.addLine(to: CGPoint(x: r, y: r - wee))
            p.move(to: CGPoint(x: r, y: r + wee))
            p.addLine(to: CGPoint(x: r, y: 2 * r))
        }
        
        return p.stroke(Color.black, lineWidth: 3)
            .offset(cursorOffset(diameter: diameter))
    }
    
    private func cursorOffset(diameter: CGFloat) -> CGSize {
        CGSize(width: cursor.x - diameter / 2,
               height: cursor.y - diameter / 2)
    }
}

#Preview {
    PixelPickerView()
}

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接