let nsImage:NSImage = NSImage(byReferencingURL: …) let cgImage:CGImage = nsImage.CGImageForProposedRect(nil,context: nil,hints: nil)! let bitmapInfo:CGBitmapInfo = CGImageGetBitmapInfo(cgImage) Swift.print(bitmapInfo.contains(CGBitmapInfo.ByteOrderDefault)) // True
我的kCGBitmapByteOrder32Host是小端,这意味着像素格式也是小端 – 在这种情况下是BGRA.但是…… png格式是规范的大端,这就是字节实际排列在数据中的方式 – 与位图信息告诉我的方式相反.
有谁知道发生了什么事?当然,系统知道如何处理这个问题,因为pngs正确显示.是否有检测CGImage像素格式的防弹方式? GitHub提供完整的demo project.
P. S.我正在通过CFDataGetBytePtr缓冲区将原始像素数据复制到另一个库缓冲区中,然后进行处理和保存.为此,我需要明确指定像素格式.我正在处理的实际图像(我检查过的任何png / jpeg文件)显示正确,例如:
但是相同图像的位图信息给出了不正确的字节序信息,导致位图被处理为BGRA像素格式而不是实际的RGBA,当我处理它时,结果如下所示:
生成的图像演示了红色和蓝色像素之间的颜色交换,如果明确指定RGBA像素格式,一切都很完美,但我需要将此检测自动化.
P. P. S.文档简要提到CGColorSpace是定义像素格式/字节顺序的另一个重要变量,但我没有提到如何将它从那里拿出来.
> Wikipedia: RGBA color space – Representation
> Apple Lists: Byte Order in CGBitmapContextCreate
> Apple Lists: kCGImageAlphaPremultiplied First/Last
基于此,您可以使用以下扩展:
public enum PixelFormat { case abgr case argb case bgra case rgba } extension CGBitmapInfo { public static var byteOrder16Host: CGBitmapInfo { return CFByteOrderGetCurrent() == Int(CFByteOrderLittleEndian.rawValue) ? .byteOrder16Little : .byteOrder16Big } public static var byteOrder32Host: CGBitmapInfo { return CFByteOrderGetCurrent() == Int(CFByteOrderLittleEndian.rawValue) ? .byteOrder32Little : .byteOrder32Big } } extension CGBitmapInfo { public var pixelFormat: PixelFormat? { // AlphaFirst – the alpha channel is next to the red channel,argb and bgra are both alpha first formats. // AlphaLast – the alpha channel is next to the blue channel,rgba and abgr are both alpha last formats. // LittleEndian – blue comes before red,bgra and abgr are little endian formats. // Little endian ordered pixels are BGR (BGRX,XBGR,BGRA,ABGR,BGR). // BigEndian – red comes before blue,argb and rgba are big endian formats. // Big endian ordered pixels are RGB (XRGB,RGBX,ARGB,RGBA,RGB). let alphaInfo: CGImageAlphaInfo? = CGImageAlphaInfo(rawValue: self.rawValue & type(of: self).alphaInfoMask.rawValue) let alphaFirst: Bool = alphaInfo == .premultipliedFirst || alphaInfo == .first || alphaInfo == .noneSkipFirst let alphaLast: Bool = alphaInfo == .premultipliedLast || alphaInfo == .last || alphaInfo == .noneSkipLast let endianLittle: Bool = self.contains(.byteOrder32Little) // This is slippery… while byte order host returns little endian,default bytes are stored in big endian // format. Here we just assume if no byte order is given,then simple RGB is used,aka big endian,though… if alphaFirst && endianLittle { return .bgra } else if alphaFirst { return .argb } else if alphaLast && endianLittle { return .abgr } else if alphaLast { return .rgba } else { return nil } } }
请注意,您应始终注意色彩空间 – 它会直接影响原始像素数据的存储方式. CGColorSpace(名称:CGColorSpace.sRGB)可能是最安全的 – 它以普通格式存储颜色,例如,如果处理红色RGB,它将被存储就像那样(255,0),而设备颜色空间将给出你喜欢(235,73,53).
要在实践中看到这一点,请将上方和下方放入游乐场.你需要两个带有alpha和不带的单像素红色图像,this和this应该可以工作.
import AppKit import CoreGraphics extension CFData { public var pixelComponents: [UInt8] { let buffer: UnsafeMutablePointer<UInt8> = UnsafeMutablePointer.allocate(capacity: 4) defer { buffer.deallocate(capacity: 4) } CFDataGetBytes(self,CFRange(location: 0,length: CFDataGetLength(self)),buffer) return Array(UnsafeBufferPointer(start: buffer,count: 4)) } } let color: NSColor = .red Thread.sleep(forTimeInterval: 2) // Must flip coordinates to capture what we want… let screen: NSScreen = NSScreen.screens.first(where: { $0.frame.contains(NSEvent.mouseLocation) })! let rect: CGRect = CGRect(origin: CGPoint(x: NSEvent.mouseLocation.x - 10,y: screen.frame.height - NSEvent.mouseLocation.y),size: CGSize(width: 1,height: 1)) Swift.print("Will capture image with \(rect) frame.") let screenImage: CGImage = CGWindowListCreateImage(rect,[],kCGNullWindowID,[])! let urlImageWithAlpha: CGImage = NSImage(byReferencing: URL(fileURLWithPath: "/Users/ianbytchek/Downloads/red-pixel-with-alpha.png")).cgImage(forProposedRect: nil,hints: nil)! let urlImageNoAlpha: CGImage = NSImage(byReferencing: URL(fileURLWithPath: "/Users/ianbytchek/Downloads/red-pixel-no-alpha.png")).cgImage(forProposedRect: nil,hints: nil)! Swift.print(screenImage.colorSpace!,screenImage.bitmapInfo,screenImage.bitmapInfo.pixelFormat!,screenImage.dataProvider!.data!.pixelComponents) Swift.print(urlImageWithAlpha.colorSpace!,urlImageWithAlpha.bitmapInfo,urlImageWithAlpha.bitmapInfo.pixelFormat!,urlImageWithAlpha.dataProvider!.data!.pixelComponents) Swift.print(urlImageNoAlpha.colorSpace!,urlImageNoAlpha.bitmapInfo,urlImageNoAlpha.bitmapInfo.pixelFormat!,urlImageNoAlpha.dataProvider!.data!.pixelComponents) let formats: [CGBitmapInfo.RawValue] = [ CGImageAlphaInfo.premultipliedFirst.rawValue,CGImageAlphaInfo.noneSkipFirst.rawValue,CGImageAlphaInfo.premultipliedLast.rawValue,CGImageAlphaInfo.noneSkipLast.rawValue,] for format in formats { // This "paints" and prints out components in the order they are stored in data. let context: CGContext = CGContext(data: nil,width: 1,height: 1,bitsPerComponent: 8,bytesPerRow: 32,space: CGColorSpace(name: CGColorSpace.sRGB)!,bitmapInfo: format)! let components: UnsafeBufferPointer<UInt8> = UnsafeBufferPointer(start: context.data!.assumingMemoryBound(to: UInt8.self),count: 4) context.setFillColor(red: 1 / 0xFF,green: 2 / 0xFF,blue: 3 / 0xFF,alpha: 1) context.fill(CGRect(x: 0,y: 0,height: 1)) Swift.print(context.colorSpace!,context.bitmapInfo,context.bitmapInfo.pixelFormat!,Array(components)) }
这将输出以下内容.注意屏幕捕获的图像与磁盘加载的图像有何不同.
Will capture image with (285.7734375,294.5,1.0,1.0) frame. <CGColorSpace 0x7fde4e9103e0> (kCGColorSpaceICCBased; kCGColorSpaceModelRGB; iMac) CGBitmapInfo(rawValue: 8194) bgra [27,13,252,255] <CGColorSpace 0x7fde4d703b20> (kCGColorSpaceICCBased; kCGColorSpaceModelRGB; Color LCD) CGBitmapInfo(rawValue: 3) rgba [235,53,255] <CGColorSpace 0x7fde4e915dc0> (kCGColorSpaceICCBased; kCGColorSpaceModelRGB; Color LCD) CGBitmapInfo(rawValue: 5) rgba [235,255] <CGColorSpace 0x7fde4d60d390> (kCGColorSpaceICCBased; kCGColorSpaceModelRGB; sRGB IEC61966-2.1) CGBitmapInfo(rawValue: 2) argb [255,1,2,3] <CGColorSpace 0x7fde4d60d390> (kCGColorSpaceICCBased; kCGColorSpaceModelRGB; sRGB IEC61966-2.1) CGBitmapInfo(rawValue: 6) argb [255,3] <CGColorSpace 0x7fde4d60d390> (kCGColorSpaceICCBased; kCGColorSpaceModelRGB; sRGB IEC61966-2.1) CGBitmapInfo(rawValue: 1) rgba [1,3,255] <CGColorSpace 0x7fde4d60d390> (kCGColorSpaceICCBased; kCGColorSpaceModelRGB; sRGB IEC61966-2.1) CGBitmapInfo(rawValue: 5) rgba [1,255]