从 CGImage 获取像素格式

2024-03-17

我非常了解位图布局和像素格式主题,但在处理通过加载的 png / jpeg 图像时遇到问题NSImage– 我无法弄清楚我得到的是预期行为还是错误。

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

My kCGBitmapByteOrder32Host是小尾数,这意味着像素格式也是小尾数 - 在本例中为 BGRA。但是……根据规范,png 格式是大端字节序,这就是字节在数据中的实际排列方式 - 与位图信息告诉我的相反。

有人知道发生了什么事吗?当然,系统知道如何处理这个问题,因为 png 可以正确显示。有没有一种防弹的方法来检测 CGImage 的像素格式?完全的演示项目 https://github.com/ianbytchek/cgbitmapinfo-byte-order-bug可在 GitHub 上获取。


P. S.我正在通过复制原始像素数据CFDataGetBytePtr将缓冲区放入另一个库缓冲区中,然后对其进行处理并保存。为此,我需要明确指定像素格式。我正在处理的实际图像(我检查过的任何 png/jpeg 文件)可以正确显示,例如:

但是相同图像的位图信息给了我不正确的字节序信息,导致位图被处理为 BGRA 像素格式而不是实际的 RGBA,当我处理它时,结果如下所示:

生成的图像演示了红色和蓝色像素之间的颜色交换,如果明确指定 RGBA 像素格式,一切都会完美运行,但我需要自动执行此检测。


P. P. S.文档简要提到CGColorSpace是定义像素格式/字节顺序的另一个重要变量,但我发现没有提及如何将其从那里取出。


几年后,在生产中测试我的发现后,我可以充满信心地分享它们,但希望具有理论知识的人能在这里更好地解释事情吗?刷新记忆的好地方:

  • 维基百科:RGBA 色彩空间 – 表示 https://en.wikipedia.org/wiki/RGBA_color_space#Representatiom
  • Apple 列表:CGBitmapContextCreate 中的字节顺序 https://web.archive.org/web/20160822071631/https://lists.apple.com/archives/quartz-dev/2009/May/msg00032.html
  • Apple 列表:kCGImageAlphaPremultiplied First/Last https://web.archive.org/web/20160909120259/http://lists.apple.com/archives/carbon-dev/2006/Nov/msg00014.html

在此基础上,您可以使用以下扩展:

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(name: CGColorSpace.sRGB)可能是最安全的 - 它以纯格式存储颜色,例如,如果您处理红色 RGB,它将像 (255, 0, 0) 一样存储,而设备颜色空间将为您提供类似 (235, 73, 53)。

要在实践中看到这一点,请将上方和下方放入游乐场中。您需要两张带 Alpha 和不带 Alpha 的单像素红色图像,this https://i.stack.imgur.com/RdR97.png and this https://i.stack.imgur.com/150G7.png应该管用。

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, context: nil, hints: nil)!
let urlImageNoAlpha: CGImage = NSImage(byReferencing: URL(fileURLWithPath: "/Users/ianbytchek/Downloads/red-pixel-no-alpha.png")).cgImage(forProposedRect: nil, context: 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, width: 1, 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, 73, 53, 255]
<CGColorSpace 0x7fde4e915dc0> (kCGColorSpaceICCBased; kCGColorSpaceModelRGB; Color LCD) CGBitmapInfo(rawValue: 5) rgba [235, 73, 53, 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, 1, 2, 3]
<CGColorSpace 0x7fde4d60d390> (kCGColorSpaceICCBased; kCGColorSpaceModelRGB; sRGB IEC61966-2.1) CGBitmapInfo(rawValue: 1) rgba [1, 2, 3, 255]
<CGColorSpace 0x7fde4d60d390> (kCGColorSpaceICCBased; kCGColorSpaceModelRGB; sRGB IEC61966-2.1) CGBitmapInfo(rawValue: 5) rgba [1, 2, 3, 255]
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

从 CGImage 获取像素格式 的相关文章

随机推荐

  • 内置 html 编码的 HTML 编辑器

    我现在使用 Notepad 似乎经常在这里建议进行基本的 html 和 css 编辑 唯一缺少的是粘贴到文本中的 HTML 编码方法 有没有办法在 Notepad 中执行此操作 或者我是否需要寻找其他编辑器来执行此操作 如果有什么建议的话
  • 如何让 SpannableStringBuilder 附加格式化字符串内的范围?

    背景 假设我使用 SpannableStringBuilder 将多个内容附加到其中 其中之一是我从 strings xml 文件格式化的字符串 该文件内部有一个跨度 SpannableStringBuilder stringBuilder
  • 在 HTML 页面中显示 PHP 回显消息

    您好 我是网络开发的新手 我正在尝试在 html 页面中显示来自 php 文件的回显消息 PHP 文件 其他文件
  • 如何使用 .htaccess 获得漂亮的链接[重复]

    这个问题在这里已经有答案了 可能的重复 htaccess 创建友好的 URL 需要帮助 https stackoverflow com questions 3033407 htacces to create friendly urls he
  • 如何从 Facebook 应用程序发布到 Facebook 页面?

    我想发布来自的图像数据脸书应用程序 to 脸书页面 我尝试使用流 发布 我能够发布图像 但它已被弃用 而且我也无法发帖link 使用stream publish发布的Curl请求如下 卷曲 F access token dsdsdsd F
  • 无法反序列化当前 JSON 数组(例如 [1,2,3])

    我正在尝试读取我的 JSON 结果 这是我的根对象 public class RootObject public int id get set public bool is default get set public string nam
  • 使用 Jquery 首次单击时禁用提交按钮

    我目前有一个附加到提交按钮的灯箱弹出窗口 仅在第一次单击提交按钮时显示 基本上 在有人提交表单之前 我们希望他们在点击提交按钮时看到这个弹出窗口 一切工作正常 但现在我需要将其设置为第一次单击时表单不提交 处理的位置 但是 在第一次单击后
  • 为插入语句生成uuid

    我想像这样生成uuide58ed763 928c 4155 bee9 fdbaaadc15f3当数据插入表中时 CREATE TABLE IF NOT EXISTS bin lists id uuid NOT NULL bin intege
  • 如何将 astro 组件渲染为 HTML 字符串?

    我希望能够在 Astro 中拥有一个动态页面来呈现 Astro 组件 我深入研究了文档和代码 但找不到像下面这样的函数 Astro render 理想情况下 我可以将属性传递给它 我正在寻找类似的东西react renderToString
  • Android 上有哪些角色可用?

    我想在我的 Android 应用程序中显示一些特殊的 Unicode 字符 这些字符未在 droidfonts com 上列出 但它们在模拟器和我的手机上正确显示 但想确定是否所有 Android 平台都支持它们 它记录在某处吗 谢谢 ED
  • 将 Flutter Row 的子级拉伸到最大*自然*高度

    override Widget build BuildContext context return Scaffold appBar AppBar title Text title body ListView children
  • Scrapy 不会抓取所有页面

    这是我的工作代码 from scrapy item import Item Field class Test2Item Item title Field from scrapy http import Request from scrapy
  • 如果 kNN 没有训练阶段,当我们将 .fit() 方法应用于 Scikit-learn 中的 kNN 模型时会发生什么?

    由于 kNN 在 RAM 级别处理训练和预测 并且不需要显式的训练过程 那么当拟合 knn 模型时到底会发生什么 我认为这一步与训练模型有关 谢谢 这是如果我跳过拟合步骤将会得到的错误 NotFittedError This KNeighb
  • 如何在 Spring 中管理对象池?

    据我了解 在 Spring 中 所有对象默认都被视为单例 如果 singleton 设置为 false 则每个请求都会提供一个新对象 但是如果我想池化对象怎么办 假设设置范围从最少 1 到最多 10 个实例 使用 Spring 可以吗 池化
  • 我的 AndroidManifest.xml 中声明的活动的 ActivityNotFoundException

    我有一个启动的 Android 应用程序 大部分时间都运行良好 然而 在大约 1000 多次运行中 有 1 次我收到 android content ActivityNotFoundException 无法找到显式活动类异常 该活动已在我的
  • 将所有“\n”替换为“,”

    我有一个文本区域 我想将其值中的 n 替换为 var valuetxtarr txtarr val var valuetxtarrs valuetxtarr replace n g alert valuetxtarrs 但它不起作用 为什么
  • 是否可以在 PYTHON 的一个命令中对多个字符串使用 .count ?

    我想知道是否可以使用 count 函数对多个字符串进行计数 string abcdefg string count or 当我使用 or 命令时 它只给出 1 个变量的计数 但我想要总数 如何将它组合起来 使其计入 2 个字符串 而不将其拆
  • 自定义 Lint 检查未运行

    我目前正在尝试编写我正在处理的自定义 lint 检查 我创建了一个单独的 java 项目并将其作为 jar 包含在内 我的问题是 无论如何 在分析我的代码库时 似乎我的自定义检查没有运行 我已经包含了一个注册表 class MyIssueR
  • 检测堆栈溢出

    操作系统如何检测用户空间程序的堆栈溢出 然后将 SIGTERM 或 SIGSEGV 发送到这些用户空间程序 防护页 当操作系统为程序创建堆栈时 它将分配比指定的多一点的空间 内存按页分配 通常每页 4KB 并且额外的页将进行设置 以便任何访
  • 从 CGImage 获取像素格式

    我非常了解位图布局和像素格式主题 但在处理通过加载的 png jpeg 图像时遇到问题NSImage 我无法弄清楚我得到的是预期行为还是错误 let nsImage NSImage NSImage byReferencingURL let