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

pgky5nke  于 12个月前  发布在  iOS
关注(0)|答案(1)|浏览(170)

我有一个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,例如:

(262.3333282470703, 691.6666564941406)
UIExtendedSRGBColorSpace 0 0 0 0


经过长时间的搜索,我没有找到一种方法来从SwiftUI中的图像中获取像素颜色,如果有人有解决方案或可以帮助我,那将是非常好的。
先谢谢你了。

w8f9ii69

w8f9ii691#

这可能是一个比必要的更复杂的解决方案,但它有效。
基本上,它使用ImageRenderer创建Image的基于内存的副本,而可见Image的实际尺寸由GeometryReader捕获。大小的差异用于缩放拖动位置。
然后可以查询基于内存的视图的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()
}

字符串
x1c 0d1x的数据

相关问题