在 Swift 中实现 UIImage 上的擦除/恢复绘图

问题描述

我正在尝试制作一个简单的图像橡皮擦工具,用户可以在其中擦除和恢复绘制到图像中,就像在此图像中一样:

enter image description here

经过多次尝试和测试,我在UI端使用以下代码实现了足够的“擦除”功能

// Drawing code - on user touch
// `currentPath` is a `UIBezierPath` property of the containing class.
guard let image = pickedImage else { return }
UIGraphicsBeginImageContextWithOptions(imageView.frame.size,false,0)
if let context = UIGraphicsGetCurrentContext() {
    mainImageView.layer.render(in: context)
    context.addpath(currentPath.cgPath)
    context.setBlendMode(.clear)
    context.setlinewidth(translatedBrushWidth)
    context.setLineCap(.round)
    context.setLineJoin(.round)
    context.setstrokeColor(UIColor.clear.cgColor)
    context.strokePath()

    let capturedImage = UIGraphicsGetimageFromCurrentimageContext()
    imageView.image = capturedImage
}

UIGraphicsEndImageContext()

用户修饰时,我对 currentPath 应用缩放变换,以全尺寸渲染带有剪切部分的图像,以保持 UI 性能

我现在想弄清楚的是如何处理“恢复”功能。本质上,用户应该在擦除的部分上绘制以显示原始图像。
我试过查看 CGContextClipToMask,但我不确定如何实现。

我还研究了在渲染实际图像之前实现这种“擦除/恢复”效果的其他方法,例如在图像上屏蔽 CAShapeLayer,但在这方法中恢复也成为一个问题。

>

非常感谢任何帮助,以及使用 UI 级别和渲染级别的路径擦除和恢复的替代方法。 谢谢!

解决方法

是的,我建议将 CALayer 添加到您的图像层作为蒙版。

您可以将遮罩层设为 CAShapeLayer 并在其中绘制几何形状,也可以使用简单的 CALayer 作为遮罩,其中遮罩层的 contents 属性是CGImage。然后将不透明像素绘制到遮罩中以显示图像内容,或将透明像素绘制到“擦除”相应的图像像素中。

这种方法是硬件加速的,而且速度相当快。

处理橡皮擦功能的撤消/重做需要您收集对遮罩层的更改以及遮罩的先前状态。

编辑:

我创建了 a small demo app on Github 来展示如何使用 CGImage 作为图像视图上的遮罩

这是该项目的自述文件:


MaskableImageView

该项目演示了如何使用 CALayer 屏蔽 UIView

它定义了 UIImageView 的一个自定义子类,MaskableView

MaskableView 类具有包含 CALayer 的属性 maskLayer

MaskableView 在其 didSet 属性上定义了一个 bounds 方法,以便当视图的边界发生变化时,它会调整遮罩层的大小以匹配图像视图的大小。

MaskableView 有一个方法 installSampleMask,它构建一个与图像视图大小相同的图像,大部分填充不透明的黑色,但中心有一个小矩形填充黑色,alpha 为0.7.半透明的中心矩形使图像视图部分透明并显示下面的视图。

演示应用程序在 MaskableView 中安装了几个子视图、Scampers 的示例图像、我的一只狗和一个 UILabel。它还在 MaskableView 下方安装了棋盘格图像,以便您可以更轻松地看到半透明部分。

MaskableView 具有 circleRadiusmaskDrawingAlphadrawingAction 属性,用于让用户通过点击要更新的视图来擦除/取消擦除图像面具。

MaskableViewUIPanGestureRecognizerUITapGestureRecognizer 附加到自身,操作为 gestureRecognizerUpdategestureRecognizerUpdate 方法从手势识别器中获取点击/拖动位置,并使用它在图像蒙版上绘制一个圆圈,该圆要么减少图像蒙版的 alpha(以部分擦除像素)或增加图像蒙版的 alpha(以使那些像素更不透明。)

MaskableView 的蒙版图很粗糙,仅用于演示目的。它绘制了一系列离散的圆圈,而不是根据用户的拖动手势将路径渲染到遮罩中。更好的解决方案是连接来自手势识别器的点,并使用它们将平滑曲线渲染到蒙版中。

应用程序的屏幕如下所示:

enter image description here