ios - CGContext:如何删除位图上下文之外的像素(例如 kCGBlendModeClear)?

标签 ios objective-c core-graphics cgcontext

我正在尝试使用 Core Graphics 构建橡皮擦工具,但我发现制作高性能橡皮擦非常困难 - 这一切都归结为:

CGContextSetBlendMode(上下文, kCGBlendModeClear)

如果您在谷歌上搜索如何使用 Core Graphics“删除”,几乎每个答案都会返回该片段。问题是它只能(显然)在位图上下文中工作。如果你正在尝试实现交互式删除,我看不出 kCGBlendModeClear 如何帮助你 - 据我所知,你或多或少被锁定在屏幕上和屏幕外删除 UIImage/CGImage 并在著名的非性能 [UIView drawRect] 中绘制该图像。

这是我能做到的最好的:

-(void)drawRect:(CGRect)rect
{
    if (drawingStroke) {
        if (eraseModeOn) {
            UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0.0);
            CGContextRef context = UIGraphicsGetCurrentContext();
            [eraseImage drawAtPoint:CGPointZero];
            CGContextAddPath(context, currentPath);
            CGContextSetLineCap(context, kCGLineCapRound);
            CGContextSetLineWidth(context, lineWidth);
            CGContextSetBlendMode(context, kCGBlendModeClear);
            CGContextSetLineWidth(context, ERASE_WIDTH);
            CGContextStrokePath(context);
            curImage = UIGraphicsGetImageFromCurrentImageContext();
            UIGraphicsEndImageContext();
            [curImage drawAtPoint:CGPointZero];
        } else {
            [curImage drawAtPoint:CGPointZero];
            CGContextRef context = UIGraphicsGetCurrentContext();
            CGContextAddPath(context, currentPath);
            CGContextSetLineCap(context, kCGLineCapRound);
            CGContextSetLineWidth(context, lineWidth);
            CGContextSetBlendMode(context, kCGBlendModeNormal);
            CGContextSetStrokeColorWithColor(context, lineColor.CGColor);
            CGContextStrokePath(context);
        }
    } else {
        [curImage drawAtPoint:CGPointZero];
    }
}

绘制一条普通线 (!eraseModeOn) 是可以接受的性能;我正在将我的屏幕外绘图缓冲区(curImage,其中包含所有以前绘制的笔画)blit 到当前的 CGContext,并且我正在渲染线(路径)当前绘制。它并不完美,但是嘿,它可以工作,而且性能相当不错。

但是,因为 kCGBlendModeNormal 显然不能在位图上下文之外工作,所以我不得不:

  1. 创建位图上下文 (UIGraphicsBeginImageContextWithOptions)。
  2. 绘制我的屏幕外缓冲区(eraseImage,当橡皮擦工具打开时,它实际上是从 curImage 派生的 - 所以实际上与 curImage 为了论证)。
  3. 渲染当前绘制到位图上下文的“删除线”(路径)(使用 kCGBlendModeClear 清除像素)。
  4. 将整个图像提取到屏幕外缓冲区中(curImage = UIGraphicsGetImageFromCurrentImageContext();)
  5. 最后将屏幕外缓冲区 blit 到 View 的 CGContext

从性能方面来说,这太糟糕了。使用 Instrument 的时间工具,很明显这种方法的问题在于:

  • UIGraphicsBeginImageContextWithOptions 很昂贵
  • 绘制屏幕外缓冲区两次是昂贵的
  • 将整个图像提取到屏幕外缓冲区的成本很高

很自然地,代码在真实 iPad 上的表现非常糟糕。

我不太确定在这里做什么。我一直在尝试弄清楚如何在非位图上下文中清除像素,但据我所知,依赖 kCGBlendModeClear 是死路一条。

有什么想法或建议吗?其他 iOS 绘图应用如何处理删除?


附加信息

我一直在尝试使用 CGLayer 方法,因为看起来 CGContextSetBlendMode(context, kCGBlendModeClear) 将在 CGLayer 基于我所做的一些谷歌搜索。

但是,我不太希望这种方法能够成功。在 drawRect 中绘制图层(即使使用 setNeedsDisplayInRect)性能非常差; Core Graphics 将在 CGContextDrawLayerAtPoint 中渲染图层中的每个路径(根据 Instruments)。据我所知,就性能而言,这里使用位图上下文绝对更可取 - 当然,唯一的问题是上述问题(kCGBlendModeClear 在我将位图上下文 blit 到drawRect 中的主要 CGContext

最佳答案

我已经通过使用以下代码获得了良好的结果:

- (void)drawRect:(CGRect)rect
{
    if (drawingStroke) {
        if (eraseModeOn) {
            CGContextRef context = UIGraphicsGetCurrentContext();
            CGContextBeginTransparencyLayer(context, NULL);
            [eraseImage drawAtPoint:CGPointZero];

            CGContextAddPath(context, currentPath);
            CGContextSetLineCap(context, kCGLineCapRound);
            CGContextSetLineWidth(context, ERASE_WIDTH);
            CGContextSetBlendMode(context, kCGBlendModeClear);
            CGContextSetStrokeColorWithColor(context, [[UIColor clearColor] CGColor]);
            CGContextStrokePath(context);
            CGContextEndTransparencyLayer(context);
        } else {
            [curImage drawAtPoint:CGPointZero];
            CGContextRef context = UIGraphicsGetCurrentContext();
            CGContextAddPath(context, currentPath);
            CGContextSetLineCap(context, kCGLineCapRound);
            CGContextSetLineWidth(context, self.lineWidth);
            CGContextSetBlendMode(context, kCGBlendModeNormal);
            CGContextSetStrokeColorWithColor(context, self.lineColor.CGColor);
            CGContextStrokePath(context);
        }
    } else {
        [curImage drawAtPoint:CGPointZero];
    }

    self.empty = NO;
}

诀窍是将以下内容包装到 CGContextBeginTransparencyLayer/CGContextEndTransparencyLayer 调用中:

  • 将删除背景图像 block 传送到上下文
  • 使用 kCGBlendModeClear
  • 在删除背景图像之上绘制“删除”路径

由于删除背景图的像素数据和删除路径在同一层,所以起到清除像素的作用。

关于ios - CGContext:如何删除位图上下文之外的像素(例如 kCGBlendModeClear)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19876856/

相关文章:

ios - 找不到Ionic 3应用程式的'Branch.h'文件Xcode错误

ios - 在 uitableviewcell 上更快地加载图像,从互联网下载

ios - Swift 3 以编程方式绘制 uiimage

uikit - 核心图形和 UIKit 交互

swift - 在315°圆弧上绘制圆角

ios - 我可以从左到右有两个 ECSlidingViewController Drawer 吗?

android - 为证书固定存储公钥有多安全?

objective-c - Cocoa (Mac Os X) 打印多页,为什么预览窗口显示 2 页而不是 1 页?

ios - 为 ImageView 设置渐变背景,旋转

ios - UIButton 作为 UIBarButttonItem 放置在导航栏上,点击时不会触发方法