【问题标题】:How do I get the color of a pixel in a UIImage with Swift?如何使用 Swift 获取 UIImage 中像素的颜色?
【发布时间】:2014-08-05 19:13:04
【问题描述】:

我正在尝试使用 Swift 获取 UIImage 中像素的颜色,但它似乎总是返回 0。这是代码,翻译自 @Minas 在this 线程上的回答:

func getPixelColor(pos: CGPoint) -> UIColor {
    var pixelData = CGDataProviderCopyData(CGImageGetDataProvider(self.CGImage))
    var data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)

    var pixelInfo: Int = ((Int(self.size.width) * Int(pos.y)) + Int(pos.x)) * 4

    var r = CGFloat(data[pixelInfo])
    var g = CGFloat(data[pixelInfo+1])
    var b = CGFloat(data[pixelInfo+2])
    var a = CGFloat(data[pixelInfo+3])

    return UIColor(red: r, green: g, blue: b, alpha: a)
}

提前致谢!

【问题讨论】:

  • 我错了还是宽度必须是 4 的倍数?当图像宽度不是 4 的倍数时,我得到了错误的结果。我解决了我的问题,将图像(宽度)重新缩放到最接近 4 的倍数。

标签: ios uiimage swift


【解决方案1】:

由于我遇到了类似的问题,因此进行了一些搜索。 你的代码工作正常。问题可能来自您的图像。

代码:

  //On the top of your swift 
  extension UIImage {
      func getPixelColor(pos: CGPoint) -> UIColor {

          let pixelData = CGDataProviderCopyData(CGImageGetDataProvider(self.CGImage))
          let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)

          let pixelInfo: Int = ((Int(self.size.width) * Int(pos.y)) + Int(pos.x)) * 4

          let r = CGFloat(data[pixelInfo]) / CGFloat(255.0)
          let g = CGFloat(data[pixelInfo+1]) / CGFloat(255.0)
          let b = CGFloat(data[pixelInfo+2]) / CGFloat(255.0)
          let a = CGFloat(data[pixelInfo+3]) / CGFloat(255.0)

          return UIColor(red: r, green: g, blue: b, alpha: a)
      }  
  }

这个方法会从图像的 CGImage 中选择像素颜色。因此,请确保您从正确的图像中挑选。例如如果您的 UIImage 是 200x200,但来自 Imgaes.xcassets 或来自任何地方的原始图像文件是 400x400,并且您正在选择点 (100,100),那么您实际上是在选择图像左上角的点,而不是中间。

两种解决方案:
1、使用Imgaes.xcassets中的图片,在1x字段中只放一张@1x图片。将@2x、@3x 留空。确保您知道图像大小,并在范围内选择一个点。

//Make sure only 1x image is set
let image : UIImage = UIImage(named:"imageName") 
//Make sure point is within the image
let color : UIColor = image.getPixelColor(CGPointMake(xValue, yValue)) 

2,将 CGPoint 放大/缩小比例以匹配 UIImage。例如let point = CGPoint(100,100)在上面的例子中,

let xCoordinate : Float = Float(point.x) * (400.0/200.0)
let yCoordinate : Float = Float(point.y) * (400.0/200.0) 
let newCoordinate : CGPoint = CGPointMake(CGFloat(xCoordinate), CGFloat(yCoordinate))
let image : UIImage = largeImage
let color : UIColor = image.getPixelColor(CGPointMake(xValue, yValue)) 

我只测试了第一种方法,我正在使用它从调色板中获取颜色。两者都应该工作。 快乐编码:)

【讨论】:

  • 不错。这次真是万分感谢!我建议将您的 var 更改为 let ,因为无需对其进行变异。
  • 是否可以保证数据在预期的 RGBA 颜色空间中?我找不到任何确定的文档。
  • 这个解决方案对我有用,但后来发生了一些变化,数据以 ARGB 而不是 RGBA 的形式出现。查看此链接以获取更多信息和解决方案:stackoverflow.com/questions/34593706/…
  • 作为旁注。确保images.xcassets 中的图像是.png 格式。我尝试使用pdf 并得到了一些真正的不稳定结果。
  • 我正在捕捉 iPhone6 屏幕的快照,然后将生成的图像分配给 imageView(x:20,y:20,w:343,h:38)。此代码适用于 y=0,但我无法使其工作(即)x=342,y=37。我正在调用 ...getPixelColot(CGPoint(x:342*2, y:37*2)。我乘 *2 因为视网膜显示,但它不起作用,有什么线索吗?
【解决方案2】:

SWIFT 3、XCODE 8 经过测试并可以正常工作

extension UIImage {
    func getPixelColor(pos: CGPoint) -> UIColor {

        let pixelData = self.cgImage!.dataProvider!.data
        let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)

        let pixelInfo: Int = ((Int(self.size.width) * Int(pos.y)) + Int(pos.x)) * 4

        let r = CGFloat(data[pixelInfo]) / CGFloat(255.0)
        let g = CGFloat(data[pixelInfo+1]) / CGFloat(255.0)
        let b = CGFloat(data[pixelInfo+2]) / CGFloat(255.0)
        let a = CGFloat(data[pixelInfo+3]) / CGFloat(255.0)

        return UIColor(red: r, green: g, blue: b, alpha: a)
    }

}

【讨论】:

  • 我不得不稍微修改一下,我的图像没有 alpha 通道,所以我删除了 alpha 线并将 pos.x 乘以 3 而不是 4。效果很好!跨度>
  • 什么是pos?什么元素提供 x 和 y 值?
  • pos 是您请求颜色的“位置”。 CGPoint 有 x 和 y 值。您可以在 UIImage 上将其称为“let color = myImage.getPixelColor(CGPoint(x:30,y:40))”。
  • !很容易出错......并且您的整个应用程序崩溃。我可以建议返回 UIColor 吗?并将let pixelData = self.cgImage!.dataProvider!.data 更改为guard let pixelData = self.cgImage?.dataProvider?.data else { return nil }
【解决方案3】:

如果您多次调用已回答的问题,则不应在每个像素上都使用该函数,因为您正在重新创建同一组数据。如果您想要图像中的所有颜色,请执行以下操作:

func findColors(_ image: UIImage) -> [UIColor] {
    let pixelsWide = Int(image.size.width)
    let pixelsHigh = Int(image.size.height)

    guard let pixelData = image.cgImage?.dataProvider?.data else { return [] }
    let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)

    var imageColors: [UIColor] = []
    for x in 0..<pixelsWide {
        for y in 0..<pixelsHigh {
            let point = CGPoint(x: x, y: y)
            let pixelInfo: Int = ((pixelsWide * Int(point.y)) + Int(point.x)) * 4
            let color = UIColor(red: CGFloat(data[pixelInfo]) / 255.0,
                                green: CGFloat(data[pixelInfo + 1]) / 255.0,
                                blue: CGFloat(data[pixelInfo + 2]) / 255.0,
                                alpha: CGFloat(data[pixelInfo + 3]) / 255.0)
            imageColors.append(color)
        }
    }
    return imageColors
}

这是Example Project

附带说明,这个函数比公认的答案要快得多,但它给出的结果不太明确。我只是将 UIImageView 放在 sourceView 参数中。

func getPixelColorAtPoint(point: CGPoint, sourceView: UIView) -> 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)

    sourceView.layer.render(in: context!)
    let color: UIColor = 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(capacity: 4)
    return color
}

【讨论】:

  • 我正在获取完整 UIImage 的 RGB 数据...感谢 Sethmr。
  • @Sethmr 你能解释一下这是如何工作的吗?因为我需要对角线上的颜色(从 topLeft 到 bottomRight),所以我有一个对角线的 CGPoints 数组。
  • @Bhushan 已经有一段时间了,但基本上,第一个迭代 UIImage 中的像素,而第二个我从其他地方得到,现在有点不确定它在做什么。就点而言,如果您将 CGPoint 数组的 x 和 y 值乘以 UIScreen.main.scale,那么您应该能够获取该值并使用 在 y 循环的顶部进行检查保护 yourPointArray.contains(CGPoint(x: x, y: y) else { return } 只将你想要的颜色添加到数组中。
  • 我在这一行收到以下错误Thread 1: EXC_BAD_ACCESS (code=1, address=0x112aa6180)let color = UIColor(red: CGFloat(data[pixelInfo]) / 255.0, green: CGFloat(data[pixelInfo + 1]) / 255.0, blue: CGFloat(data[pixelInfo + 2]) / 255.0, alpha: CGFloat(data[pixelInfo + 3]) / 255.0)。你知道这是什么原因造成的吗?
  • 谢谢@Sethmr。当我运行您的确切项目时,我没有遇到任何问题。当我在我的项目中运行从手机相册中读取图像的代码时,我遇到了同样的问题。您对看什么有什么建议吗?
【解决方案4】:

我把颜色换成了红色和蓝色。 原始函数也没有考虑每行的实际字节数和每像素的字节数。 我也尽可能避免打开可选选项。 这是一个更新的函数。

import UIKit

extension UIImage {
    /// Get the pixel color at a point in the image
    func pixelColor(atLocation point: CGPoint) -> UIColor? {
        guard let cgImage = cgImage, let pixelData = cgImage.dataProvider?.data else { return nil }

        let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)

        let bytesPerPixel = cgImage.bitsPerPixel / 8

        let pixelInfo: Int = ((cgImage.bytesPerRow * Int(point.y)) + (Int(point.x) * bytesPerPixel))

        let b = CGFloat(data[pixelInfo]) / CGFloat(255.0)
        let g = CGFloat(data[pixelInfo+1]) / CGFloat(255.0)
        let r = CGFloat(data[pixelInfo+2]) / CGFloat(255.0)
        let a = CGFloat(data[pixelInfo+3]) / CGFloat(255.0)

        return UIColor(red: r, green: g, blue: b, alpha: a)
    }
}

【讨论】:

  • bgr 将很常见,更好(因为您为 bitsPerPixel、bytesPerRow 等添加了 ests)将检查颜色空间并打开它(例如,kCGColorSpaceDeviceRGB)
【解决方案5】:

Swift3 (IOS 10.3)

重要提示:-这仅适用于 @1x 图像。

请求:-

如果您有 @2x@3x 图片的解决方案,请分享。谢谢你:)

extension UIImage {

    func getPixelColor(atLocation location: CGPoint, withFrameSize size: CGSize) -> UIColor {
        let x: CGFloat = (self.size.width) * location.x / size.width
        let y: CGFloat = (self.size.height) * location.y / size.height

        let pixelPoint: CGPoint = CGPoint(x: x, y: y)

        let pixelData = self.cgImage!.dataProvider!.data
        let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)

        let pixelIndex: Int = ((Int(self.size.width) * Int(pixelPoint.y)) + Int(pixelPoint.x)) * 4

        let r = CGFloat(data[pixelIndex]) / CGFloat(255.0)
        let g = CGFloat(data[pixelIndex+1]) / CGFloat(255.0)
        let b = CGFloat(data[pixelIndex+2]) / CGFloat(255.0)
        let a = CGFloat(data[pixelIndex+3]) / CGFloat(255.0)

        return UIColor(red: r, green: g, blue: b, alpha: a)
    }

}

用法

print(yourImageView.image!.getPixelColor(atLocation: location, withFrameSize: yourImageView.frame.size))

您可以使用 tapGestureRecognizer 进行定位。

【讨论】:

  • 将 x 和 y 与 self.scale 相乘。这将应用所使用图像的比例
【解决方案6】:

你的代码对我来说很好,作为 UIImage 的扩展。你是如何测试你的颜色的?这是我的例子:

    let green = UIImage(named: "green.png")
    let topLeft = CGPoint(x: 0, y: 0)

    // Use your extension
    let greenColour = green.getPixelColor(topLeft)

    // Dump RGBA values
    var redval: CGFloat = 0
    var greenval: CGFloat = 0
    var blueval: CGFloat = 0
    var alphaval: CGFloat = 0
    greenColour.getRed(&redval, green: &greenval, blue: &blueval, alpha: &alphaval)
    println("Green is r: \(redval) g: \(greenval) b: \(blueval) a: \(alphaval)")

打印出来:

    Green is r: 0.0 g: 1.0 b: 1.0 a: 1.0

...这是正确的,因为我的图像是一个纯绿色的正方形。

(“它似乎总是返回 0”是什么意思?你不会碰巧在黑色像素上进行测试吧?)

【讨论】:

    【解决方案7】:

    在交换 R 和 B 方面,我正在倒退颜色,不知道为什么我认为顺序是 RGBA。

    func testGeneratedColorImage() {
    
        let color = UIColor(red: 0.5, green: 0, blue: 1, alpha: 1)
        let size = CGSize(width: 10, height: 10)
        let image = UIImage.image(fromColor: color, size: size)
    
        XCTAssert(image.size == size)
    
        XCTAssertNotNil(image.cgImage)
    
        XCTAssertNotNil(image.cgImage!.dataProvider)
    
        let pixelData = image.cgImage!.dataProvider!.data
        let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)
    
        let position = CGPoint(x: 1, y: 1)
        let pixelInfo: Int = ((Int(size.width) * Int(position.y)) + Int(position.x)) * 4
    
        let r = CGFloat(data[pixelInfo]) / CGFloat(255.0)
        let g = CGFloat(data[pixelInfo+1]) / CGFloat(255.0)
        let b = CGFloat(data[pixelInfo+2]) / CGFloat(255.0)
        let a = CGFloat(data[pixelInfo+3]) / CGFloat(255.0)
    
        let testColor = UIColor(red: r, green: g, blue: b, alpha: a)
    
        XCTAssert(testColor == color, "Colour: \(testColor) does not match: \(color)")
    }
    

    color 看起来像这样:

    image 看起来像这样:

    testColor 看起来像:

    (我可以理解,蓝色值可能有点偏离,并且是浮点数不准确的 0.502)

    代码切换到:

        let b = CGFloat(data[pixelInfo]) / CGFloat(255.0)
        let g = CGFloat(data[pixelInfo+1]) / CGFloat(255.0)
        let r = CGFloat(data[pixelInfo+2]) / CGFloat(255.0)
        let a = CGFloat(data[pixelInfo+3]) / CGFloat(255.0)
    

    我得到testColor 为:

    【讨论】:

    • 是的,我也需要交换 r & b!!
    • 您需要检查 CGImage 的 CGColorSpace ...它可能是 kCGColorSpaceDeviceRGB 或者它可能是 kCGColorSpaceDeviceBGRA 或 ...(等等)
    【解决方案8】:

    我认为您需要将每个组件除以 255:

    var r = CGFloat(data[pixelInfo]) / CGFloat(255.0)
    var g = CGFloat(data[pixelInfo + 1]) / CGFloat(255.0)
    var b = CGFloat(data[pixelInfo + 2]) / CGFloat(255.0)
    var a = CGFloat(data[pixelInfo + 3]) / CGFloat(255.0)
    

    【讨论】:

      【解决方案9】:

      我试图找出图像所有四个角的颜色,却得到了意想不到的结果,包括UIColor.clear

      问题是像素从 0 开始,因此请求图像宽度的像素实际上会回绕并给我第二行的第一个像素。

      例如,640 x 480 图像的右上角像素实际上是x: 639, y: 0,右下角像素实际上是x: 639, y: 479

      这是我通过此调整实现的 UIImage 扩展:

      func getPixelColor(pos: CGPoint) -> UIColor {
      
          guard let cgImage = cgImage, let pixelData = cgImage.dataProvider?.data else { return UIColor.clear }
          let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)
      
          let bytesPerPixel = cgImage.bitsPerPixel / 8
          // adjust the pixels to constrain to be within the width/height of the image
          let y = pos.y > 0 ? pos.y - 1 : 0
          let x = pos.x > 0 ? pos.x - 1 : 0
          let pixelInfo = ((Int(self.size.width) * Int(y)) + Int(x)) * bytesPerPixel
      
          let r = CGFloat(data[pixelInfo]) / CGFloat(255.0)
          let g = CGFloat(data[pixelInfo+1]) / CGFloat(255.0)
          let b = CGFloat(data[pixelInfo+2]) / CGFloat(255.0)
          let a = CGFloat(data[pixelInfo+3]) / CGFloat(255.0)
      
          return UIColor(red: r, green: g, blue: b, alpha: a)
      }
      

      【讨论】:

        【解决方案10】:

        Swift 5,包括 @2x@3x 图像的解决方案

        extension UIImage {
            subscript(_ point: CGPoint) -> UIColor? {
                guard let pixelData = self.cgImage?.dataProvider?.data else { return nil }
                let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)
                let pixelInfo: Int = Int((size.width * point.y + point.x) * 4.0 * scale * scale)
                let i = Array(0 ... 3).map { CGFloat(data[pixelInfo + $0]) / CGFloat(255) }
                return UIColor(red: i[0], green: i[1], blue: i[2], alpha: i[3])
            }
        }
        

        【讨论】:

        • 包含的解决方案如何工作?您的解决方案有何特别之处?
        【解决方案11】:

        我使用这个扩展:

        public extension UIImage {
        
            var pixelWidth: Int {
                return cgImage?.width ?? 0
            }
        
            var pixelHeight: Int {
                return cgImage?.height ?? 0
            }
        
            func pixelColor(x: Int, y: Int) -> UIColor {
        
                if 0..<pixelWidth ~= x && 0..<pixelHeight ~= y {
                    log.info("Pixel coordinates are in bounds")
                }else {
                    log.info("Pixel coordinates are out of bounds")
                                return .black
                }
                guard
                    let cgImage = cgImage,
                    let data = cgImage.dataProvider?.data,
                    let dataPtr = CFDataGetBytePtr(data),
                    let colorSpaceModel = cgImage.colorSpace?.model,
                    let componentLayout = cgImage.bitmapInfo.componentLayout
                else {
                    assertionFailure("Could not get a pixel of an image")
                    return .clear
                }
        
                assert(
                    colorSpaceModel == .rgb,
                    "The only supported color space model is RGB")
                assert(
                    cgImage.bitsPerPixel == 32 || cgImage.bitsPerPixel == 24,
                    "A pixel is expected to be either 4 or 3 bytes in size")
        
                let bytesPerRow = cgImage.bytesPerRow
                let bytesPerPixel = cgImage.bitsPerPixel/8
                let pixelOffset = y*bytesPerRow + x*bytesPerPixel
        
                if componentLayout.count == 4 {
                    let components = (
                        dataPtr[pixelOffset + 0],
                        dataPtr[pixelOffset + 1],
                        dataPtr[pixelOffset + 2],
                        dataPtr[pixelOffset + 3]
                    )
        
                    var alpha: UInt8 = 0
                    var red: UInt8 = 0
                    var green: UInt8 = 0
                    var blue: UInt8 = 0
        
                    switch componentLayout {
                    case .bgra:
                        alpha = components.3
                        red = components.2
                        green = components.1
                        blue = components.0
                    case .abgr:
                        alpha = components.0
                        red = components.3
                        green = components.2
                        blue = components.1
                    case .argb:
                        alpha = components.0
                        red = components.1
                        green = components.2
                        blue = components.3
                    case .rgba:
                        alpha = components.3
                        red = components.0
                        green = components.1
                        blue = components.2
                    default:
                        return .clear
                    }
        
                    // If chroma components are premultiplied by alpha and the alpha is `0`,
                    // keep the chroma components to their current values.
                    if cgImage.bitmapInfo.chromaIsPremultipliedByAlpha && alpha != 0 {
                        let invUnitAlpha = 255/CGFloat(alpha)
                        red = UInt8((CGFloat(red)*invUnitAlpha).rounded())
                        green = UInt8((CGFloat(green)*invUnitAlpha).rounded())
                        blue = UInt8((CGFloat(blue)*invUnitAlpha).rounded())
                    }
        
                    return .init(red: red, green: green, blue: blue, alpha: alpha)
        
                } else if componentLayout.count == 3 {
                    let components = (
                        dataPtr[pixelOffset + 0],
                        dataPtr[pixelOffset + 1],
                        dataPtr[pixelOffset + 2]
                    )
        
                    var red: UInt8 = 0
                    var green: UInt8 = 0
                    var blue: UInt8 = 0
        
                    switch componentLayout {
                    case .bgr:
                        red = components.2
                        green = components.1
                        blue = components.0
                    case .rgb:
                        red = components.0
                        green = components.1
                        blue = components.2
                    default:
                        return .clear
                    }
        
                    return .init(red: red, green: green, blue: blue, alpha: UInt8(255))
        
                } else {
                    assertionFailure("Unsupported number of pixel components")
                    return .clear
                }
            }
        
        }
        

        但是对于正确的像素颜色,您只需要在 x1 中使用 xcasset 中的图像,否则您的参考是错误的,您需要使用这个:为你点。

        【讨论】:

          【解决方案12】:

          https://stackoverflow.com/a/40237504/3286489 的解决方案,仅适用于 sRGB 颜色空间类型的图像。但是,对于不同的色彩空间(扩展 sRGB??),它不起作用。

          因此,要使其正常工作,需要先将其转换为普通的 sRGB 图像类型,然后再从 cgImage 获取颜色。注意,我们需要在计算中添加padding,以确保宽度始终为 8 倍

          public extension UIImage {
              func getPixelColor(pos: CGPoint) -> UIColor {
                  
                  // convert to standard sRGB image
                  guard let cgImage = cgImage,
                      let colorSpace = CGColorSpace(name: CGColorSpace.sRGB),
                      let context = CGContext(data: nil, 
                          width: Int(size.width), height: Int(size.height),
                          bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace,
                          bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)
                  else { return .white }
          
                  context.draw(cgImage, in: CGRect(origin: .zero, size: size))
                  
                  // Get the newly converted cgImage
                  guard let newCGImage = context.makeImage(),
                      let newDataProvider = newCGImage.dataProvider,
                      let data = newDataProvider.data
                  else { return .white }
                  let pixelData: UnsafePointer<UInt8> = CFDataGetBytePtr(data)
          
                  // Calculate the pixel position based on point given
                  let remaining = 8 - ((Int(size.width)) % 8)
                  let padding = (remaining < 8) ? remaining : 0
                  let pixelInfo: Int = (((Int(size.width) + padding) * Int(pos.y)) + Int(pos.x)) * 4
                  
                  let r = CGFloat(pixelData[pixelInfo]) / CGFloat(255.0)
                  let g = CGFloat(pixelData[pixelInfo+1]) / CGFloat(255.0)
                  let b = CGFloat(pixelData[pixelInfo+2]) / CGFloat(255.0)
                  let a = CGFloat(pixelData[pixelInfo+3]) / CGFloat(255.0)
                  return UIColor(red: r, green: g, blue: b, alpha: a)
             }
          }
          

          或者,如果不想转换为cgImage,只需替换

                  // Get the newly converted cgImage
                  guard let newCGImage = context.makeImage(),
                      let newDataProvider = newCGImage.dataProvider,
                      let newData = newDataProvider.data
                      else { return .white }
                  let pixelData: UnsafePointer<UInt8> = CFDataGetBytePtr(newData)
          

                  // Get the data and bind it from UnsafeMutableRawPointer to UInt8
                  guard let data = context.data else { return .white }
                  let pixelData = data.bindMemory(
                      to: UInt8.self, capacity: Int(size.width * size.height * 4))
          

          更新

          为了获得更简洁的代码,我们可以直接使用UIGraphicsImageRenderer 改进对sRGB 的转换。由于这种重绘将像素进一步细化为 2 倍,因此计算确实发生了一些变化。

              func getPixelColor(pos: CGPoint) -> UIColor {
                  
                  let newImage = UIGraphicsImageRenderer(size: size).image { _ in
                      draw(in: CGRect(origin: .zero, size: size))
                  }
                  
                  guard let cgImage = newImage.cgImage,
                      let dataProvider = cgImage.dataProvider,
                      let data = dataProvider.data else { return .white }
                  let pixelData: UnsafePointer<UInt8> = CFDataGetBytePtr(data)
                  let remaining = 8 - ((Int(size.width) * 2) % 8)
                  let padding = (remaining < 8) ? remaining : 0
                  let pixelInfo: Int = (((Int(size.width * 2) + padding) * Int(pos.y * 2)) + Int(pos.x * 2)) * 4
                          
                  let r = CGFloat(pixelData[pixelInfo]) / CGFloat(255.0)
                  let g = CGFloat(pixelData[pixelInfo+1]) / CGFloat(255.0)
                  let b = CGFloat(pixelData[pixelInfo+2]) / CGFloat(255.0)
                  let a = CGFloat(pixelData[pixelInfo+3]) / CGFloat(255.0)
                  return UIColor(red: r, green: g, blue: b, alpha: a)
              }
          

          这是根据https://stackoverflow.com/a/64538344/3286489中转换为sRGB的解决方案

          【讨论】:

          【解决方案13】:

          我在互联网上的任何地方都找不到提供的答案

          • 简单代码
          • HDR 支持
          • 对 bgr 等的颜色配置文件支持
          • 对@2x @3x 的扩展支持

          原来如此。据我所知,最终解决方案:

          斯威夫特 5

          
          import UIKit
          
          public extension CGBitmapInfo {
            // https://stackoverflow.com/a/60247693/2585092
            enum ComponentLayout {
              case bgra
              case abgr
              case argb
              case rgba
              case bgr
              case rgb
          
              var count: Int {
                switch self {
                case .bgr, .rgb: return 3
                default: return 4
                }
              }
            }
          
            var componentLayout: ComponentLayout? {
              guard let alphaInfo = CGImageAlphaInfo(rawValue: rawValue & Self.alphaInfoMask.rawValue) else { return nil }
              let isLittleEndian = contains(.byteOrder32Little)
          
              if alphaInfo == .none {
                return isLittleEndian ? .bgr : .rgb
              }
              let alphaIsFirst = alphaInfo == .premultipliedFirst || alphaInfo == .first || alphaInfo == .noneSkipFirst
          
              if isLittleEndian {
                return alphaIsFirst ? .bgra : .abgr
              } else {
                return alphaIsFirst ? .argb : .rgba
              }
            }
          
            var chromaIsPremultipliedByAlpha: Bool {
              let alphaInfo = CGImageAlphaInfo(rawValue: rawValue & Self.alphaInfoMask.rawValue)
              return alphaInfo == .premultipliedFirst || alphaInfo == .premultipliedLast
            }
          }
          
          extension UIImage {
            // https://stackoverflow.com/a/68103748/2585092
            subscript(_ point: CGPoint) -> UIColor? {
              guard
                let cgImage = cgImage,
                let space = cgImage.colorSpace,
                let pixelData = cgImage.dataProvider?.data,
                let layout = cgImage.bitmapInfo.componentLayout
              else {
                return nil
              }
              let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)
          
              let comp = CGFloat(layout.count)
              let hdr = CGFloat(space.isHDR() ? 2 : 1)
              let pixelInfo = Int((size.width * point.y * scale + point.x * scale) * comp * hdr)
              let i = Array(0 ... Int(comp - 1)).map {
                CGFloat(data[pixelInfo + $0 * Int(hdr)]) / CGFloat(255)
              }
          
              switch layout {
              case .bgra:
                return UIColor(red: i[2], green: i[1], blue: i[0], alpha: i[3])
              case .abgr:
                return UIColor(red: i[3], green: i[2], blue: i[1], alpha: i[0])
              case .argb:
                return UIColor(red: i[1], green: i[2], blue: i[3], alpha: i[0])
              case .rgba:
                return UIColor(red: i[0], green: i[1], blue: i[2], alpha: i[3])
              case .bgr:
                return UIColor(red: i[2], green: i[1], blue: i[0], alpha: 1)
              case .rgb:
                return UIColor(red: i[0], green: i[1], blue: i[2], alpha: 1)
              }
            }
          }
          
          

          【讨论】:

            【解决方案14】:

            像往常一样,聚会迟到了,但我想提一下,上述方法并不总是有效。如果图像不是 RGBA,那么它可能会崩溃。根据我的经验,当调试代码正常运行时,运行发布(优化)代码可能会崩溃。

            我倾向于在我的应用程序中使用大量矢量图像,而 iOS 有时可以在单色空间中渲染它们。我经历了许多崩溃,这里给出的代码。

            此外,我们应该在垂直跳过时使用 bytesPerRow。 Apple 倾向于为位图添加填充,简单的 4 字节像素偏移可能不起作用。

            我将图像绘制到屏幕外上下文中,然后从那里获取样本。

            这就是我所做的。它有效,但并不完全有效。就我而言,这很好,因为我只在启动时使用它一次:

            extension UIImage {
                /* ################################################################## */
                /**
                 This returns the RGB color (as a UIColor) of the pixel in the image, at the given point. It is restricted to 32-bit (RGBA/8-bit pixel) values.
                 This was inspired by several of the answers [in this StackOverflow Question](https://stackoverflow.com/questions/25146557/how-do-i-get-the-color-of-a-pixel-in-a-uiimage-with-swift).
                 **NOTE:** This is unlikely to be highly performant!
                 
                 - parameter at: The point in the image to sample (NOTE: Must be within image bounds, or nil is returned).
                 - returns: A UIColor (or nil).
                 */
                func getRGBColorOfThePixel(at inPoint: CGPoint) -> UIColor? {
                    guard (0..<size.width).contains(inPoint.x),
                          (0..<size.height).contains(inPoint.y)
                    else { return nil }
            
                    // We draw the image into a context, in order to be sure that we are accessing image data in our required format (RGBA).
                    UIGraphicsBeginImageContextWithOptions(size, false, 0)
                    draw(at: .zero)
                    let imageData = UIGraphicsGetImageFromCurrentImageContext()
                    UIGraphicsEndImageContext()
            
                    guard let cgImage = imageData?.cgImage,
                          let pixelData = cgImage.dataProvider?.data
                    else { return nil }
                    
                    let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)
                    let bytesPerPixel = (cgImage.bitsPerPixel + 7) / 8
                    let pixelByteOffset: Int = (cgImage.bytesPerRow * Int(inPoint.y)) + (Int(inPoint.x) * bytesPerPixel)
                    let divisor = CGFloat(255.0)
                    let r = CGFloat(data[pixelByteOffset]) / divisor
                    let g = CGFloat(data[pixelByteOffset + 1]) / divisor
                    let b = CGFloat(data[pixelByteOffset + 2]) / divisor
                    let a = CGFloat(data[pixelByteOffset + 3]) / divisor
            
                    return UIColor(red: r, green: g, blue: b, alpha: a)
                }
            }
            

            【讨论】:

              【解决方案15】:

              如果您使用来自 Imgaes.xcassets 的图像,并且在 1x 字段中只放置一个 @1x 图像。将@2x、@3x 留空。

              【讨论】:

                猜你喜欢
                • 2020-05-31
                • 1970-01-01
                • 1970-01-01
                • 2023-03-12
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多