IOS brush hardness like Photoshop

How to get the following smoothness (stiffness) effect of a brush, for example, Photoshop?

My attempt:

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

I tried to adjust the alpha values ​​and shadow blur coefficient, but failed.

Does anyone have a solution? Any help would be appreciated.

+5
source share
4 answers

In this image you can see the following code result. I believe that this is almost the same as you want.

enter image description here

It's just that the outer shadow is not even enough to give this smooth effect, so I add the inner shadow to the white shape.

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

As for the size of the shape, you should adjust the blur radius of the inner and outer shadows.

+11
source

, , ,

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

Multiply, , , :

enter image description here

, , , "" (. ).

+3

, , , , .

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

FXBlurView UIView UIImage , ( , fooobar.com/questions/26224/...):

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

FXBlurView UIImage:

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

, Photoshop .

. OpenCV, Photoshop.

+1

- ( , ).

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
0

All Articles