绘制CGImage的一部分

发布于 2024-09-15 14:26:34 字数 675 浏览 2 评论 0原文

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

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

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

问题是,CGContextDraw 仅采用目标矩形 - 并拉伸源 CGImage 以填充它。

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

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

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


考虑到缺乏源矩形,以及从 CGImage 上的矩形创建 CGImage 的容易性,我开始怀疑 CGImage 可能实现了写时复制语义,其中从 CGImage 生成的 CGImage 将引用子矩形与父级具有相同的物理位。 分析似乎证明这是错误的:/

I have an application that draws images from a CGImage.

The CImage itself is loaded using a CGImageSourceCreateImageAtIndex to create an image from a PNG file.

This forms part of a sprite engine - there are multiple sprite images on a single PNG file, so each sprite has a CGRect defining where it is found on the CGImage.

The problem is, CGContextDraw only takes a destination rect - and stretches the source CGImage to fill it.

So, to draw each sprite image we need to create multiple CGImages from the original source, using CGImageCreateWithImageInRect().

I thought at first that this would be a 'cheap' operation - it doesn't seem necessary for each CGImage to contain its own copy of the images bits - however, profiling has revealed that using CGImageCreateWithImageInRect() is a rather expensive operation.

Is there a more optimal method to draw a sub-section of a CGImage onto a CGContext so I dont need to CGImageCreateWithImageInRect() so often?


Given the lack of a source rectangle, and the ease of making a CGImage from a rect on a CGImage I began to suspect that perhaps CGImage implemented a copy-on-write semantic where a CGImage made from a CGImage would refer to a sub-rect of the same physical bits as the parent.
Profiling seems to prove this wrong :/

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

余罪 2024-09-22 14:26:35

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

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

  1. 剪辑和翻译对 CPU 的消耗过高。太慢了。似乎增加位图数据量只会对性能产生轻微影响,这表明这种方法缺乏任何可扩展性。

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

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

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

I was in the same boat as you. CGImageCreateWithImageInRect() worked better for my needs but previously I had attempted to convert to an NSImage, and prior to that I was clipping the context I was drawing in, and translating so that CGContextDrawImage() would draw the right data into the clipped region.

Of all of the solutions I tried:

  1. Clipping and translating was prohibitively tolling on the CPU. It was too slow. It seemed that increasing the amount of bitmap data only slightly made significant performance impacts, suggesting that this approach lacks any sort of scalability.

  2. Conversion to NSImage was relatively efficient, at least for the data we were using. There didn't seem to be any duplication of bitmap data that I could see, which was mostly what I was afraid of going from one image object to another.

  3. At one point I converted to a CIImage, as this class also allows drawing subregions of the image. This seemed to be slower than converting to NSImage, but did offer me the chance to fiddle around with the bitmap by passing through some of the Core Image filters.

  4. Using CGImageCreateWithImageInRect() was the fastest of the lot; maybe this has been optimised since you had last used it. The documentation for this function says the resulting image retains a reference to the original image, this seems to agree with what you had assumed regarding copy-on-write semantics. In my benchmarks, there appears to be no duplication of data but maybe I'm reading the results wrong. We went with this method because it was not only the fastest but it seemed like a more “clean” approach, keeping the whole process in one framework.

你爱我像她 2024-09-22 14:26:35

我相信建议是使用剪切区域。

I believe the recommendation is to use a clipping region.

白馒头 2024-09-22 14:26:35

在编写简单的 2D 基于图块的游戏时,我遇到了类似的问题。

我获得良好性能的唯一方法是:

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

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

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

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

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

I had a similar problem when writing a simple 2D tile-based game.

The only way I got decent performance was to:

1) Pre-render the tilesheet CGImage into a CGBitmapContext using CGContextDrawImage()

2) Create another CGBitmapContext as an offscreen rendering buffer, with the same size as the UIView I was drawing in, and same pixel format as the context from (1).

3) Write my own fast blit routine that would copy a region (CGRect) of pixels from the bitmap context created in (1) to the bitmap context created in (2). This is pretty easy: just simple memory copying (and some extra per-pixel operations to do alpha blending if needed), keeping in mind that the rasters are in reverse order in the buffer (the last row of pixels in the image is at the beginning of the buffer).

4) Once a frame had been drawn, draw the offscreen buffer in the view using CGContextDrawImage().

As far as I could tell, every time you call CGImageCreateWithImageInRect(), it decodes the entire PNG file into a raw bitmap, then copies the desired region of the bitmap to the destination context.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文