【问题标题】:Is drawRect "wasteful" when cropping? Is there an alternative?裁剪时drawRect“浪费”吗?有替代方案吗?
【发布时间】:2014-02-26 14:29:17
【问题描述】:

假设您有一张原始图片

200 high, 100 wide

假设您只想绘制它的一个正方形。比方说,只是底部的正方形。

假设你想将它绘制到一个新的小图像上

20 high, 20 wide

当然,您只需这样做:

CGRect imageRect = CGRectMake( -10,0, 20,20);
.. begin graphics context ..
[originalImage drawInRect:imageRect];

使用drawRect,您可以提供一个与原始图像相同的完整形状(相同比例)的矩形,但以新画布的大小。没问题。

但是:

在示例中,您将 整个原始图像 -- 整个 200 高度 绘制到新的小正方形上。

(当然,“上半部分”错过了新画布,而您只能在新画布上获得下半部分——这正是您想要的。)

我的印象是 iOS 渲染或计算“整个”原始图像,它只将下半部分(在示例中)“放到”新画布上。

这看起来很浪费。

有更快的方法吗?

好像应该有一个命令,是这样的:

drawThisPartOfTheOriginalImage: (0,100 to 100,200)
ontoThisPartOfTheNewCanvas: (0,20 to 20,20)

什么情况?当您只绘制原始图像的一小部分时,有没有比 drawRect 更有效的命令?干杯


CGContextClipToRect 方法...(不起作用!)

.

我按照 Peter 下面的建议尝试了 CGContextClipToRect。

CGContextClipToRect 确实在“结果”画布上设置了您将绘制到的区域。我只是将其设置为结果画布的大小(在上面的示例中为 20.20)。重复这里的目标是通过避免毫无意义地绘制原始的、错误的、未绘制的部分来让 iOS 节省时间。

此示例用于将原始图像 2000.2000 绘制到 500.500(即,仅将原始图像的左上四分之一绘制到结果上)。

实际上请注意,当您包含 CGContextClipToRect 时它会稍微慢一些,这再次表明 iOS“知道何时停止”。

// no need to "overdraw"... quickener turned OFF
//CGContextRef c = UIGraphicsGetCurrentContext();
//CGContextClipToRect(c, CGRectMake(0, 0, resultSize.width,resultSize.height));
//Execution Time .................................. 0.443669

// no need to "overdraw"... quickener turned ON
CGContextRef c = UIGraphicsGetCurrentContext();
CGContextClipToRect(c, CGRectMake(0, 0, resultSize.width,resultSize.height));
//Execution Time .................................. 0.461845

正如您所见,实际上,添加 CGContextClipToRect 技巧会慢一点。

为了记录,这里是用于裁剪图像的确切例程:

-(UIImage *)simplishTopCrop:(UIImage *)fromImage
 {
 // check for zero fromImage.size.width etc etc
 
 CGSize resultSize = CGSizeMake(640,640);
 
 CGFloat scale = MAX(
  resultSize.width/fromImage.size.width,
  resultSize.height/fromImage.size.height);
 
 CGFloat width = fromImage.size.width * scale;
 CGFloat height = fromImage.size.height * scale;
 CGRect imageRect = CGRectMake(0,0, width,height);
 
 UIGraphicsBeginImageContextWithOptions(resultSize, NO, 0);
 
 // INSERT 'CGContextClipToRect' TRICK ABOVE, RIGHT HERE
 
 [fromImage drawInRect:imageRect];
 UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();    
 UIGraphicsEndImageContext();
 return newImage;
 }

【问题讨论】:

  • 相差约 20 毫秒?您是否多次运行测试? 20 毫秒可能在正常误差范围内。
  • 嗨,彼得,当然,我们对它进行了非常、非常、非常广泛的试验。烦人的是,没有时间节省。正如我所说,平均而言,它实际上比简单地省略两行 CGContextClipToRect 代码慢一点(你没有注意到)。我的猜测是,在内部,iOS“知道”无论如何都不会“过度扫描”,所以(显然)非常可悲的是,调用 CGContextClipToRect 完全没有意义。

标签: ios ios7 core-graphics drawrect


【解决方案1】:

这就是剪辑的用武之地。剪辑到您的脏矩形,然后将整个图像绘制到您的范围内。剪切路径将至少阻止图像的其余部分出现,并希望完全不会被合成或采样。

如果您在 Instruments 中的分析发现效率不够高,您可以尝试使用CGImageCreateWithImageInRect 裁剪图像本身,然后将该图像绘制到您的脏矩形中。您可能希望保留裁剪后的图像,并且仅在 rect 更改时将其丢弃。一种或另一种方式,裁剪图像可能更有效——但不要忘记在前后进行分析以证明这一点。

【讨论】:

  • @JoeBlow: CGContextClipToRect.
  • 啊,关于你的第一段。事实上 CGContextClipToRect 并没有帮助。因此,我添加了代码以使用 CGContextClipToRect 基本上剪辑到新结果图像的整个大小。因此,“避免”绘制到目标尺寸以外的尺寸(如问题中所述)。我的结果,不幸的是它需要同样的时间! (实际上一根头发更长,这表明实际上 iOS 无论如何“知道”这样做。)我现在将调查您的第二个建议!
  • 一个 CGContext 必须已经剪辑到它的边界。毕竟,上下文后面的图形缓冲区具有固定大小,因此在负坐标(或从侧面掉落的坐标)处绘制会在缓冲区之外绘制像素并在没有隐式裁剪的情况下破坏内存。因此,当它同时执行隐式剪辑和另一次运行以剪辑到您给它的矩形时,您所添加的只是开销。
  • @uliwitness:所以,您是说显式剪辑到脏矩形会导致上下文检查两个矩形的坐标,而不是它们的交集?
  • 嗨@uliwitness:我不确定我是否同意你的看法。我们完全不知道它在内部是如何工作的。例如:它可能是这样实现的:(示例实现)显然)它没有写它。考虑到诸如 drawToRect 之类的拓扑例程的荒谬多样性,这将是非常明智/可靠的方法。 如果就是这样:回想一下,我问的是非常具体的情况......对不起,继续......
猜你喜欢
  • 2012-05-29
  • 2021-12-03
  • 2019-07-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多