You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

macOS SwiftUI中如何通过CGPoint获取图像像素RGB值?

获取macOS SwiftUI Image上光标位置的RGB像素值

以下是适配macOS的完整解决方案,核心是将SwiftUI Image转换为CGImage以访问原始像素数据,并处理视图坐标到图片像素坐标的转换:

import SwiftUI

struct ImageView: View {
    @State var location: CGPoint = .zero
    @State var didHover = false
    @State var pixelRGB: (red: Int, green: Int, blue: Int)? = nil
    
    // 从Asset加载图片并转换为CGImage(macOS下需通过NSImage中转)
    private let cgImage: CGImage? = {
        guard let nsImage = NSImage(named: "Burros_1") else { return nil }
        return nsImage.cgImage(forProposedRect: nil, context: nil, hints: nil)
    }()
    
    var body: some View {
        VStack {
            Text("Os três")
                .font(.system(size: 16))
            
            // 用GeometryReader获取视图布局信息,用于坐标转换
            GeometryReader { geo in
                Image("Burros_1")
                    .resizable()
                    .scaledToFit()
                    .aspectRatio(contentMode: .fit)
                    .background(Color.black.opacity(0.5))
                    .frame(width: 640, height: 480)
                    .border(.green)
                    .onHover { hover in
                        hover ? NSCursor.crosshair.push() : NSCursor.pop()
                    }
                    .onContinuousHover { phase in
                        switch phase {
                        case .active(let location):
                            NSCursor.crosshair.push()
                            self.location = location
                            self.didHover = true
                            // 计算当前光标位置对应的像素RGB值
                            self.pixelRGB = getPixelRGB(at: location, in: geo.size)
                        case .ended:
                            self.didHover = false
                            self.pixelRGB = nil
                        }
                    }
            }
            .frame(width: 640, height: 480)
            
            if didHover {
                let row = Int(round(location.x))
                let col = Int(round(location.y))
                let rgbText = pixelRGB.map { "(R: \($0.red), G: \($0.green), B: \($0.blue))" } ?? "??"
                Text("(r, c, RGB) \(String(format: "%03d", row)), \(String(format: "%03d", col)), \(rgbText)")
                    .monospaced()
            } else {
                Text(" ")
            }
        }
    }
    
    // 核心函数:将视图坐标转换为图片原始像素坐标并读取RGB值
    private func getPixelRGB(at point: CGPoint, in viewSize: CGSize) -> (red: Int, green: Int, blue: Int)? {
        guard let image = cgImage else { return nil }
        
        // 1. 计算图片在视图中的实际显示区域(处理scaledToFit的留白)
        let imageAspect = CGFloat(image.width) / CGFloat(image.height)
        let viewAspect = viewSize.width / viewSize.height
        
        let imageFrame: CGRect
        if imageAspect > viewAspect {
            // 图片宽高比大于视图,高度填满视图,左右留白
            let scaledHeight = viewSize.height
            let scaledWidth = scaledHeight * imageAspect
            let xOffset = (viewSize.width - scaledWidth) / 2
            imageFrame = CGRect(x: xOffset, y: 0, width: scaledWidth, height: scaledHeight)
        } else {
            // 图片宽高比小于视图,宽度填满视图,上下留白
            let scaledWidth = viewSize.width
            let scaledHeight = scaledWidth / imageAspect
            let yOffset = (viewSize.height - scaledHeight) / 2
            imageFrame = CGRect(x: 0, y: yOffset, width: scaledWidth, height: scaledHeight)
        }
        
        // 2. 检查光标是否在图片显示区域内
        guard imageFrame.contains(point) else { return nil }
        
        // 3. 将视图坐标转换为图片原始像素坐标
        let normalizedX = (point.x - imageFrame.minX) / imageFrame.width
        let normalizedY = (point.y - imageFrame.minY) / imageFrame.height
        
        let pixelX = Int(normalizedX * CGFloat(image.width))
        let pixelY = Int(normalizedY * CGFloat(image.height))
        
        // 确保像素坐标在图片范围内
        guard pixelX >= 0, pixelX < image.width, pixelY >= 0, pixelY < image.height else { return nil }
        
        // 4. 读取CGImage的像素字节数据
        guard let dataProvider = image.dataProvider,
              let data = dataProvider.data,
              let bytes = CFDataGetBytePtr(data) else { return nil }
        
        // 计算目标像素的字节偏移(默认按BGRA格式处理,若颜色偏差可调整顺序)
        let bytesPerPixel = image.bitsPerPixel / 8
        let bytesPerRow = image.bytesPerRow
        let pixelOffset = pixelY * bytesPerRow + pixelX * bytesPerPixel
        
        let blue = Int(bytes[pixelOffset])
        let green = Int(bytes[pixelOffset + 1])
        let red = Int(bytes[pixelOffset + 2])
        
        return (red: red, green: green, blue: blue)
    }
}

关键逻辑说明

  • CGImage转换:macOS下SwiftUI Image基于NSImage实现,需将其转为CGImage才能直接访问原始像素数据。
  • 坐标适配:处理scaledToFit带来的留白问题,先计算图片在视图中的实际显示区域,再将光标位置归一化后转换为图片的原始像素坐标。
  • 像素读取:通过CGDataProvider获取图片字节数据,根据CGImage的像素格式计算目标像素的字节偏移,提取RGB值(若出现颜色反转,可调整RGB的读取顺序)。
  • 边界校验:确保光标位置在图片有效区域内,避免数组越界访问。

内容的提问来源于stack exchange,提问作者Nate Lockwood

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 08:33:10