【问题标题】:Draw part of CGImage绘制CGImage的一部分
【发布时间】:2010-08-25 13:19:22
【问题描述】:

我有一个从 CGImage 中绘制图像的应用程序。

使用 CGImageSourceCreateImageAtIndex 加载 CImage 本身以从 PNG 文件创建图像。

这构成了精灵引擎的一部分 - 单个 PNG 文件上有多个精灵图像,因此每个精灵都有一个 CGRect 定义它在 CGImage 上的位置。

问题是,CGContextDraw 只接受一个目标矩形 - 并拉伸源 CGImage 以填充它。

因此,要绘制每个精灵图像,我们需要使用 CGImageCreateWithImageInRect() 从原始源创建多个 CGImage。

起初我认为这将是一个“便宜”的操作 - 每个 CGImage 似乎没有必要包含自己的图像位副本 - 但是,分析表明使用 CGImageCreateWithImageInRect() 是相当昂贵的操作。

是否有更优化的方法可以将 CGImage 的子部分绘制到 CGContext 上,这样我就不需要经常使用 CGImageCreateWithImageInRect() 了?


鉴于缺少源矩形,以及从 CGImage 上的矩形制作 CGImage 的便利性,我开始怀疑 CGImage 可能实现了写时复制语义,其中由 CGImage 制作的 CGImage 将引用与父级相同物理位的子矩形。 分析似乎证明这是错误的:/

【问题讨论】:

    标签: cocoa core-graphics cgimage


    【解决方案1】:

    我和你在同一条船上。 CGImageCreateWithImageInRect() 可以更好地满足我的需求,但之前我曾尝试转换为 NSImage,在此之前我正在剪辑我正在绘制的上下文,并进行翻译,以便 CGContextDrawImage() 将正确的数据绘制到剪辑中地区。

    在我尝试过的所有解决方案中:

    1. 剪辑和翻译对 CPU 的负担过重。它太慢了。增加位图数据量似乎只会对性能产生显着影响,这表明这种方法缺乏任何形式的可扩展性。

    2. 转换为NSImage 相对有效,至少对于我们使用的数据而言。我似乎看不到任何重复的位图数据,这主要是我害怕从一个图像对象到另一个图像对象的原因。

    3. 有一次我转换为CIImage,因为这个类还允许绘制图像的子区域。这似乎比转换为NSImage 慢,但确实让我有机会通过一些核心图像过滤器来摆弄位图。

    4. 使用CGImageCreateWithImageInRect() 是最快的;也许自从您上次使用它以来,它已经被优化了。 documentation for this function 表示生成的图像保留对原始图像的引用,这似乎与您对写时复制语义的假设一致。在我的基准测试中,似乎没有数据重复,但也许我读错了结果。我们选择了这种方法,因为它不仅速度最快,而且似乎是一种更“干净”的方法,将整个过程保持在一个框架中。

    【讨论】:

      【解决方案2】:

      【讨论】:

      • 如果我因为“低效”而避免使用的 api 被证明具有 bemroe 功能并且速度更快,那就太讽刺了。
      • CGImage 在某些方面效果更好,尤其是读取和写入光栅图像(使用 Image I/O)。但是 NSImage 在某些方面也更好,比如与分辨率无关的绘图(因为 NSImage 的大小以点为单位与其表示的像素大小相匹配)。 Mac OS X 上没有单一的全能图像类。
      【解决方案3】:

      我认为建议是使用剪切区域。

      【讨论】:

        【解决方案4】:

        我在编写一个简单的 2D 瓷砖游戏时遇到了类似的问题。

        我获得良好表现的唯一方法是:

        1) 使用 CGContextDrawImage() 将 tilesheet CGImage 预渲染到 CGBitmapContext 中

        2) 创建另一个 CGBitmapContext 作为屏幕外渲染缓冲区,大小与我绘制的 UIView 相同,像素格式与 (1) 中的上下文相同。

        3) 编写我自己的快速 blit 例程,将像素区域 (CGRect) 从 (1) 中创建的位图上下文复制到 (2) 中创建的位图上下文。这很简单:只需简单的内存复制(以及一些额外的每像素操作来进行 alpha 混合,如果需要),请记住光栅在缓冲区中的顺序是相反的(图像中的最后一行像素位于缓冲区的开头)。

        4) 绘制完一帧后,使用 CGContextDrawImage() 在视图中绘制屏幕外缓冲区。

        据我所知,每次调用 CGImageCreateWithImageInRect() 时,它都会将整个 PNG 文件解码为原始位图,然后将位图的所需区域复制到目标上下文。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-10-12
          • 1970-01-01
          • 2023-03-23
          • 1970-01-01
          • 2012-03-28
          • 2013-10-22
          相关资源
          最近更新 更多