像Photoshop一样的iOS刷机硬度

时间:2012-08-14 04:18:17

标签: ios core-graphics brush stroke

如何获得像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 个答案:

答案 0 :(得分:11)

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

enter image description here

只是外部阴影不足以提供平滑效果,这就是为什么我添加一些内部阴影来塑造白色的原因。

- (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);
}

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

答案 1 :(得分: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);

使用“乘法”混合模式,使用白色作为笔触颜色并设置想要阴影的笔刷颜色,可以得到以下结果:

enter image description here

我已经将绘图功能连接到touchesMoved事件,这样我绘制图像的一部分所花费的时间就越长,刷牙就越难了#34;绘制(见黑线)。

答案 2 :(得分: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的软刷工具的精确复制。

答案 3 :(得分:0)

我一直在努力画出内心发光的道路,并以某种方式成功(至少在我的口味下)。

我已在levinunnick's 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