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
相关产品推荐
相关产品推荐

