5

如何像photoshop一样获得以下画笔平滑度(硬度)效果?

我的尝试:

CGContextRef context = UIGraphicsGetCurrentContext(); 
CGContextSaveGState(context);
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, 30);
CGContextSetStrokeColorWithColor(context, [UIColor colorWithRed:1.0f green:0.0f blue:0.0f alpha:0.5f].CGColor);
CGContextSetShadowWithColor(context, CGSizeMake(0, 0), 20.0f, [UIColor colorWithRed:1.0f green:0.0f blue:0.0f alpha:1.0f].CGColor);
CGContextAddPath(context, path);
CGContextStrokePath(context);
CGContextRestoreGState(context);

我尝试调整 alpha 值和阴影模糊因子,但没有成功的结果。

有人对此有解决方案吗?任何帮助,将不胜感激。

4

4 回答 4

12

在此图像上,您可以看到以下代码结果。我相信这与您想要的几乎相同。

在此处输入图像描述

只是外阴影不足以产生平滑的效果,这就是为什么我添加一些内阴影来塑造白色的形状。

- (void)drawRect:(CGRect)rect {

    CGContextRef context = UIGraphicsGetCurrentContext();

    // Shadows
    UIColor* shadow = UIColor.redColor;
    CGSize shadowOffset = CGSizeMake(0.1, -0.1);
    CGFloat shadowBlurRadius = 11;
    UIColor* shadow2 = UIColor.whiteColor; // Here you can adjust softness of inner shadow.
    CGSize shadow2Offset = CGSizeMake(0.1, -0.1);
    CGFloat shadow2BlurRadius = 9;

    // Rectangle Drawing
    UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(59, 58, 439, 52) cornerRadius: 21];
    CGContextSaveGState(context);
    CGContextSetShadowWithColor(context, shadowOffset, shadowBlurRadius, [shadow CGColor]);
    [UIColor.redColor setFill];
    [rectanglePath fill];

    // Rectangle Inner Shadow
    CGContextSaveGState(context);
    UIRectClip(rectanglePath.bounds);
    CGContextSetShadowWithColor(context, CGSizeZero, 0, NULL);

    CGContextSetAlpha(context, CGColorGetAlpha([shadow2 CGColor]));
    CGContextBeginTransparencyLayer(context, NULL);
    {
        UIColor* opaqueShadow = [shadow2 colorWithAlphaComponent: 1];
        CGContextSetShadowWithColor(context, shadow2Offset, shadow2BlurRadius, [opaqueShadow CGColor]);
        CGContextSetBlendMode(context, kCGBlendModeSourceOut);
        CGContextBeginTransparencyLayer(context, NULL);

        [opaqueShadow setFill];
        [rectanglePath fill];

        CGContextEndTransparencyLayer(context);
    }
    CGContextEndTransparencyLayer(context);
    CGContextRestoreGState(context);

    CGContextRestoreGState(context);
}

关于形状的大小,您必须调整内部和外部阴影模糊半径。

于 2014-08-08T23:05:10.007 回答
4

通过将阴影与笔画混合,您可以获得类似于您想要实现的效果

CGContextRef context = UIGraphicsGetCurrentContext();
CGContextAddPath(context, path);
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, self.lineWidth);
CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextSetShadowWithColor(context, CGSizeMake(0.f, 0.f), self.lineWidth/4, [self.lineColor CGColor]);
CGContextSetBlendMode(context, kCGBlendModeMultiply);
CGContextSetAlpha(context, self.lineAlpha);
CGContextStrokePath(context);

使用正片叠底混合模式,使用白色作为笔触颜色并将您想要的画笔颜色设置为阴影,您会得到以下结果:

在此处输入图像描述

我已经将绘图功能连接到 touchesMoved 事件,这样我绘制图像的一部分所花费的时间越长,“画笔”绘制的越难(见黑线)。

于 2015-11-25T14:11:01.690 回答
1

这可能不是完美的答案,但它是我能为我的需要做的最好的。

获取 FXBlurView:https ://github.com/nicklockwood/FXBlurView

完成绘图后,您可以在 FXBlurView 上绘制笔画或将 UIView 转换为 UIImage(使用我从这个答案https://stackoverflow.com/a/22494886/505259获取的代码):

+ (UIImage *) imageWithView:(UIView *)view
{
    UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.opaque, 0.0f);
    [view drawViewHierarchyInRect:view.bounds afterScreenUpdates:NO];
    UIImage * snapshotImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return snapshotImage;
}

并在 UIImage 上使用 FXBlurView 的类别:

- (UIImage *)blurredImageWithRadius:(CGFloat)radius
                         iterations:(NSUInteger)iterations
                          tintColor:(UIColor *)tintColor;

模糊生成的图像,使其具有 Photoshop 软笔刷般的外观。

不过,我仍在寻找真正的答案。我有一个 OpenCV 项目,它需要 Photoshop 软笔刷工具的精确复制品。

于 2014-08-11T09:38:40.683 回答
0

我一直在努力绘制具有内在光芒的路径,并且以某种方式成功(至少在我看来)。

我已经在levinunnick 的 Smooth-Line-View上实现了绘图代码。该代码已获得 MIT 许可,因此您需要将其添加到您的项目中。

目前,您可以为要绘制的线条分配线条颜色、宽度和平滑度。注意平滑度,使用 0 - 1 之间的浮点数。我更改了触摸方法,因为我需要从另一个视图访问绘图方法。如果要恢复到触摸方法,请检查原始代码。

我没有优化代码,如果您有更好的主意,请编辑此答案。

这是H文件:

@interface LineView : UIView
- (instancetype)initWithFrame:(CGRect)frame andColor:(UIColor *)lineColor andWidth:(CGFloat)lineWidth andSmoothness:(CGFloat)lineSmooth;    
- (void)touchStartedWith:(CGPoint)location;    
- (void)touchMovedWith:(CGPoint)location;
@end

这是 M 文件:

#import "LineView.h"

static const CGFloat kPointMinDistance = 0.05f;
static const CGFloat kPointMinDistanceSquared = kPointMinDistance * kPointMinDistance;

@interface LineView ()
@property (strong) UIColor *lineColor;
@property (assign) CGFloat lineWidth;
@property (assign) CGFloat lineSmooth;

@property (assign) CGPoint currentPoint;
@property (assign) CGPoint previousPoint;
@property (assign) CGPoint previousPreviousPoint;
@end

@implementation LineView
{
@private
  CGMutablePathRef _path;
}

- (instancetype)initWithFrame:(CGRect)frame andColor:(UIColor *)lineColor andWidth:(CGFloat)lineWidth andSmoothness:(CGFloat)lineSmooth
{
  self = [super initWithFrame:frame];
  if ( self ) {
    _path = CGPathCreateMutable();

    if ( lineSmooth < 0 ) lineSmooth = 0;
    if ( lineSmooth > 1 ) lineSmooth = 1;

    self.backgroundColor = [UIColor clearColor];
    self.lineColor = lineColor;
    self.lineWidth = lineWidth;
    self.lineSmooth = lineWidth * ( lineSmooth / 4 );
    self.opaque = NO;
  }
  return self;
}

- (void)drawRect:(CGRect)rect
{
  [self.backgroundColor set];
  UIRectFill(rect);

  @autoreleasepool {

    CGColorRef theColor = self.lineColor.CGColor;
    UIColor *theClearOpaque = [[UIColor whiteColor] colorWithAlphaComponent:1];

    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextAddPath(context, _path);
    CGContextSetLineCap(context, kCGLineCapRound);
    CGContextSetLineWidth(context, self.lineWidth);
    CGContextSetStrokeColorWithColor(context, theColor);

    // Outer shadow
    CGSize shadowOffset = CGSizeMake(0.1f, -0.1f);
    CGFloat shadowBlurRadius = self.lineSmooth;
    CGContextSetShadowWithColor(context, shadowOffset, shadowBlurRadius, theColor);

    CGContextStrokePath(context);

    if ( self.lineSmooth > 0 ) {
      // Inner shadow
      CGRect bounds = CGPathGetBoundingBox(_path);
      CGRect drawBox = CGRectInset(bounds, -2.0f * self.lineWidth, -2.0f * self.lineWidth);

      CGContextSaveGState(context);
      UIRectClip(drawBox);
      CGContextSetShadowWithColor(context, CGSizeZero, 0, NULL);

      CGContextSetAlpha(context, CGColorGetAlpha(theClearOpaque.CGColor));
      CGContextBeginTransparencyLayer(context, NULL);
      {
        // Outer shadow
        UIColor *oShadow = [theClearOpaque colorWithAlphaComponent:1];
        CGContextSetShadowWithColor(context, CGSizeMake(0.1f, -0.1f), self.lineWidth / 64 * self.lineSmooth, oShadow.CGColor);
        CGContextSetBlendMode(context, kCGBlendModeSourceOut);
        CGContextBeginTransparencyLayer(context, NULL);

        [oShadow setFill];

        // Draw the line again
        CGContextAddPath(context, _path);
        CGContextSetLineCap(context, kCGLineCapRound);
        CGContextSetLineWidth(context, self.lineWidth);
        CGContextSetStrokeColorWithColor(context, oShadow.CGColor);
        CGContextStrokePath(context);

        CGContextEndTransparencyLayer(context);
      }
      CGContextEndTransparencyLayer(context);
      CGContextRestoreGState(context);
    }
  }
}

- (void)touchStartedWith:(CGPoint)location
{
  self.previousPoint = location;
  self.previousPreviousPoint = location;
  self.currentPoint = location;

  [self touchMovedWith:location];
}

- (void)touchMovedWith:(CGPoint)location
{
  CGRect drawBox;

  @autoreleasepool {
    CGFloat dx = location.x - self.currentPoint.x;
    CGFloat dy = location.y - self.currentPoint.y;

    if ( ( dx * dx + dy * dy ) < kPointMinDistanceSquared ) {
      return;
    }

    self.previousPreviousPoint = self.previousPoint;
    self.previousPoint = self.currentPoint;
    self.currentPoint = location;

    CGPoint mid1 = midPoint(self.previousPoint, self.previousPreviousPoint);
    CGPoint mid2 = midPoint(self.currentPoint, self.previousPoint);

    CGMutablePathRef subpath = CGPathCreateMutable();
    CGPathMoveToPoint(subpath, NULL, mid1.x, mid1.y);
    CGPathAddQuadCurveToPoint(subpath, NULL, self.previousPoint.x, self.previousPoint.y, mid2.x, mid2.y);

    CGRect bounds = CGPathGetBoundingBox(subpath);
    drawBox = CGRectInset(bounds, -2.0f * self.lineWidth, -2.0f * self.lineWidth);

    CGPathAddPath(_path, NULL, subpath);
    CGPathRelease(subpath);
  }

  [self setNeedsDisplayInRect:drawBox];
}

- (void)dealloc
{
  CGPathRelease(_path);
  _path = NULL;
}
@end
于 2015-06-04T16:37:18.817 回答