iOS创建CAShapeLayer掩码的反转

Mak*_*sim 5 drawing mask objective-c cashapelayer ios

因此,我是一名新的iOS开发人员,致力于iOS绘图应用程序,该应用程序使用x/y坐标和时间戳来回放用户草图.为了绘制简单的笔事件,我有一个UIView作为绘图视图,我使用CAShapeLayers绘制到视图,如下所示:

if (index == 0){
    CGPathMoveToPoint(path, NULL, point.x, point.y);
} else {
    CGPathAddLineToPoint(path, NULL, point.x, point.y);
}

……

CAShapeLayer *permLayer = [CAShapeLayer layer];
        [permLayer setFrame:CGRectMake(0, 0, 1024, 640)];
        [permLayer setFillColor:[[UIColor clearColor] CGColor]];
        [permLayer setStrokeColor:currentColor.CGColor];
        [permLayer setLineWidth:[event[@"strokeWeight"] floatValue]];
        [permLayer setLineJoin:kCALineJoinRound];
        [permLayer setLineCap:kCALineCapRound];

……

[[self layer] addSublayer:permLayer];

……
Run Code Online (Sandbox Code Playgroud)

一切都很好,看起来很好:

在此输入图像描述

现在我想做的是有一个看似如下的擦除事件:

在此输入图像描述

我尝试使用另一个CAShapeLayer作为掩码并将擦除事件的CGPath绘制到该掩码来完成此操作.然而,我最终得到的是:

在此输入图像描述

......这与我想要的完全相反.我想我需要以某种方式获得由擦除事件的CGPath创建的CAShapeLayer的反向掩码?我不确定接近这个的正确方法是没有我不熟悉的大量数学.

另一个需要注意的是,这个动画与CADisplayLink相关联,因此这些绘制动画经常被刷新,性能很重要.我之前尝试绘制UIImages而不是CAShapeLayers并使用kCGBlendModeClear来实现擦除效果,但性能却遭受了极大的损失.感谢您的任何建议或见解!

The*_*heo 3

我想我需要以某种方式获取由擦除事件的 CGPath 创建的 CAShapeLayer 的反掩码?

您要求的“逆掩码”不能用 a 来描述CAShapeLayer(至少没有直接的解决方案)。如果您想保留CAShapeLayer,请添加带有图像背景颜色的“擦除路径”。

但是,如果我正确理解您的实现,我会看到另一个问题:您为用户所做的每个笔划添加一个子层。如果用户用很多笔画绘制复杂的图片,最终会得到一大堆图层,这肯定会在某些时候导致性能问题。

我建议切换到另一种方法并绘制到图像缓冲区。听起来您尝试过,但由于性能原因而放弃了该方法 - 但如果做得正确,性能会相当不错。

创建您自己的位图上下文

_context = CGBitmapContextCreate(...);
Run Code Online (Sandbox Code Playgroud)

在你的 TouchesBegan/Moved 方法中绘制到该上下文,如下所示:

if(isEraseEvent)
  CGContextSetBlendMode(_context, kCGBlendModeClear);
else
  CGContextSetBlendMode(_context, kCGBlendModeNormal);

// Configure your current stroke: lineWidth, color etc.
CGContextSetLineWidth(_context, _currentStroke.lineWidth);

// Added stroke from point p to point q
CGContextMoveToPoint(_context, p.x, p.y);
CGContextAddLineToPoint(_context, q.x, q.y);
CGContextStrokePath(_context);
Run Code Online (Sandbox Code Playgroud)

在每个此类事件之后,显示上下文。您可以将整个上下文渲染到 UIImage 并显示 UIImage,但性能可能会受到这种方式的影响。相反,让 CALayer 和委托来完成渲染。让委托实现该方法

- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {
  CGImageRef tempImage = CGBitmapContextCreateImage(_context);
  CGContextDrawImage(ctx, self.bounds, tempImage);
  CGImageRelease(tempImage);
}
Run Code Online (Sandbox Code Playgroud)

不要忘记告诉图层,每当您绘制到上下文时,其内容就会发生变化。调用setNeedsDisplay会重新显示完整的图层,这在大多数情况下都超出您的需要。做这样的事情:

CGRect r = CGRectMake(MIN(p.x, q.x),
                    MIN(p.y, q.y),
                    ABS(p.x-q.x),
                    ABS(p.y-q.y));

CGFloat inset = ...;
[_theLayer setNeedsDisplayInRect:CGRectInset(r, inset, inset)];
Run Code Online (Sandbox Code Playgroud)

以确保仅重新渲染图像中内容实际发生更改的部分。inset应该是一些(负)插入来考虑你的笔划宽度。

请注意,我上面写的所有内容(上下文创建除外)都会为每个touchesMoved事件调用一次,即在用户输入笔划时渲染并显示笔划(或者,如您的情况,重播绘图过程)您永远不需要渲染或显示更多内容。