【问题标题】:CGContext.init() -- NULL color space no longer allowedCGContext.init() -- NULL 颜色空间不再允许
【发布时间】:2018-01-10 20:47:15
【问题描述】:

TL;DR:在旧版 Obj-C 代码中,颜色空间参数值为 NULL。这在 Swift 等效项中是不允许的。使用什么值?

我继承了如下代码:

unsigned char pixel[1] = {0};
CGContextRef context = CGBitmapContextCreate(
    pixel,1, 1, 8, 1, NULL, (CGBitmapInfo)kCGImageAlphaOnly
);

到 Swift 4 CGContext 的端口很简单,除了 NULL 色彩空间值。使用一个合理的值,我从CGContext.init?() 得到了nil。我的翻译是:

var pixelValue = UInt8(0)
var pixel = Data(buffer: UnsafeBufferPointer(start:&pixelValue, count:1))
let context = CGContext(
    data            : &pixel,
    width           : 1,
    height          : 1,
    bitsPerComponent: 8,
    bytesPerRow     : 1,
    space           : CGColorSpace(name:CGColorSpace.genericRGBLinear)!,
    bitmapInfo      : CGImageAlphaInfo.alphaOnly.rawValue
)! // Returns nil; unwrapping crashes

space 的适当值是多少? (我提供的值没有返回 nil;它是 CGContext() 调用本身。

设置环境变量CGBITMAP_CONTEXT_LOG_ERRORS 会产生如下错误日志:

Assertion failed: (0), function get_color_model_name, 
file /BuildRoot/Library/Caches/com.apple.xbs/Sources/Quartz2D_Sim/
Quartz2D-1129.2.1/CoreGraphics/API/CGBitmapContextInfo.c, line 210.

对于更多背景故事,上下文用于通过以下方式查找 UIImage 中单个像素的 alpha 值:

unsigned char pixel[1] = {0};
CGContextRef context = CGBitmapContextCreate(pixel,1, 1, 8, 1, NULL, (CGBitmapInfo)kCGImageAlphaOnly);
UIGraphicsPushContext(context);
[image drawAtPoint:CGPointMake(-point.x, -point.y)];
UIGraphicsPopContext();
CGContextRelease(context);
CGFloat alpha = pixel[0]/255.0;

(我确实有可能的替代方法来寻找 alpha,但为了不影响遗留代码,我希望保持这种方式。)

【问题讨论】:

  • 试试linearGray色彩空间。
  • @DonMag,我确实阅读了文档。这就是我找到ColorSpace 名称的方式。如上所示,我在通话中确实使用了它。
  • @rmaddy,谢谢,这确实允许初始化 CGContext。唉,(其余)代码的 Swift 4 版本为每个 alpha 计算 0。但我相信你回答了我的问题。
  • @AndrewDuncan,你有没有找到解决错误 alpha 计算的方法?

标签: ios swift core-graphics


【解决方案1】:

我最近研究过类似的主题,也许这个代码示例会对某人有所帮助:

let image = UIImage(named: "2.png")
guard let cgImage = image?.cgImage else {
    fatalError()
}

let width = cgImage.width
let height = cgImage.height
//CGColorSpaceCreateDeviceGray - 1 component, 8 bits
//i.e. 1px = 1byte
let bytesPerRow = width
let bitmapByteCount = width * height

let bitmapData: UnsafeMutablePointer<UInt8> = .allocate(capacity: bitmapByteCount)
defer {
    bitmapData.deallocate()
}
bitmapData.initialize(repeating: 0, count: bitmapByteCount)

guard let context = CGContext(data: bitmapData, width: width, height: height,
                              bitsPerComponent: 8, bytesPerRow: bytesPerRow,
                              space: CGColorSpaceCreateDeviceGray(), bitmapInfo: CGImageAlphaInfo.alphaOnly.rawValue) else {
                                fatalError()
}
//draw image to context
var rect = CGRect(x: 0, y: 0, width: width, height: height)
context.draw(cgImage, in: rect)

// Enumerate through all pixels
for row in 0..<height {
    for col in 0..<width {
        let alphaValue = bitmapData[row * width + col]
        if alphaValue != 0 {
            //visible pixel
        }
    }
}

【讨论】:

    【解决方案2】:

    判断一个像素是否透明的方法如下:

        let info = CGImageAlphaInfo.alphaOnly.rawValue
        let pixel = UnsafeMutablePointer<UInt8>.allocate(capacity:1)
        defer {
            pixel.deinitialize(count: 1)
            pixel.deallocate()
        }
        pixel[0] = 0
        let sp = CGColorSpaceCreateDeviceGray()
        let context = CGContext(data: pixel,
            width: 1, height: 1, bitsPerComponent: 8, bytesPerRow: 1,
            space: sp, bitmapInfo: info)!
        UIGraphicsPushContext(context)
        im.draw(at:CGPoint(-point.x, -point.y))
        UIGraphicsPopContext()
        let p = pixel[0]
        let alpha = Double(p)/255.0
        let transparent = alpha < 0.01
    

    【讨论】:

      【解决方案3】:

      为了记录,这就是我最终的做法。它还没有(还)行为不端,所以本着“如果它没有坏,就不要修复它”的原则,我会离开它。 (为了清楚起见,我添加了self。)但是您可以确定我会将马特的代码粘贴在那里,以防我将来需要它。谢谢马特!

      // Note that "self" is a UIImageView; "point" is the point under consideration.
      
      let im = self.image!
      
      // TODO: Why is this clamping necessary? We get points outside our size.
      var x = point.x
      var y = point.y
      if x < 0 { x = 0 } else if x > im.size.width  - 1 { x = im.size.width  - 1 }
      if y < 0 { y = 0 } else if y > im.size.height - 1 { y = im.size.height - 1 }
      
      let screenWidth    = self.bounds.width
      let intrinsicWidth = im.size.width
      
      x *= im.scale * intrinsicWidth/screenWidth
      y *= im.scale * intrinsicWidth/screenWidth
      
      let pixData = im.cgImage?.dataProvider?.data
      let data    = CFDataGetBytePtr(pixData!)
      
      let pixIndex = Int(((Int(im.size.width*im.scale) * Int(y)) + Int(x)) * 4)
      
      let r = data?[pixIndex]
      let g = data?[pixIndex + 1]
      let b = data?[pixIndex + 2]
      let α = data?[pixIndex + 3]
      
      let red    = CGFloat(r!)/255
      let green  = CGFloat(g!)/255
      let blue   = CGFloat(b!)/255
      let alpha  = CGFloat(α!)/255
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-12-06
        • 2017-03-09
        • 1970-01-01
        • 2021-08-20
        • 1970-01-01
        • 2012-12-13
        • 1970-01-01
        相关资源
        最近更新 更多