Closed LhiKien closed 7 years ago
Please reference to the following articles:
http://stackoverflow.com/questions/9407546/how-do-i-draw-lines-with-alpha-1-in-quartz-2d https://www.raywenderlich.com/87899/make-simple-drawing-app-uikit-swift
//
// CLDrawTool.m
//
// Created by sho yakushiji on 2014/06/20.
// Copyright (c) 2014年 CALACULU. All rights reserved.
//
#import "CLDrawTool.h"
static NSString* const kCLDrawToolEraserIconName = @"eraserIconAssetsName";
@implementation CLDrawTool
{
UIImageView *_drawingView;
UIImageView *_tempView;
CGSize _originalImageSize;
CGPoint _prevDraggingPosition;
UIView *_menuView;
UISlider *_colorSlider;
UISlider *_widthSlider;
UISlider *_alphaSlider;
UIView *_strokePreview;
UIView *_strokePreviewBackground;
UIImageView *_eraserIcon;
CLToolbarMenuItem *_colorBtn;
}
+ (NSArray*)subtools
{
return nil;
}
+ (NSString*)defaultTitle
{
return [CLImageEditorTheme localizedString:@"CLDrawTool_DefaultTitle" withDefault:@"Draw"];
}
+ (BOOL)isAvailable
{
return YES;
}
+ (CGFloat)defaultDockedNumber
{
return 4.5;
}
#pragma mark- optional info
+ (NSDictionary*)optionalInfo
{
return @{
kCLDrawToolEraserIconName : @"",
};
}
#pragma mark- implementation
- (void)setup
{
_originalImageSize = self.editor.imageView.image.size;
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(drawingViewDidPan:)];
panGesture.maximumNumberOfTouches = 1;
_drawingView = [[UIImageView alloc] initWithFrame:self.editor.imageView.bounds];
_drawingView.userInteractionEnabled = YES;
[_drawingView addGestureRecognizer:panGesture];
[self.editor.imageView addSubview:_drawingView];
_tempView = [[UIImageView alloc] initWithFrame:self.editor.imageView.bounds];
[self.editor.imageView addSubview:_tempView];
self.editor.imageView.userInteractionEnabled = YES;
self.editor.scrollView.panGestureRecognizer.minimumNumberOfTouches = 2;
self.editor.scrollView.panGestureRecognizer.delaysTouchesBegan = NO;
self.editor.scrollView.pinchGestureRecognizer.delaysTouchesBegan = NO;
_menuView = [[UIView alloc] initWithFrame:self.editor.menuView.frame];
_menuView.backgroundColor = self.editor.menuView.backgroundColor;
[self.editor.view addSubview:_menuView];
[self setMenu];
_menuView.transform = CGAffineTransformMakeTranslation(0, self.editor.view.height-_menuView.top);
[UIView animateWithDuration:kCLImageToolAnimationDuration
animations:^{
_menuView.transform = CGAffineTransformIdentity;
}];
}
- (void)cleanup
{
[_drawingView removeFromSuperview];
self.editor.imageView.userInteractionEnabled = NO;
self.editor.scrollView.panGestureRecognizer.minimumNumberOfTouches = 1;
[UIView animateWithDuration:kCLImageToolAnimationDuration
animations:^{
_menuView.transform = CGAffineTransformMakeTranslation(0, self.editor.view.height-_menuView.top);
}
completion:^(BOOL finished) {
[_menuView removeFromSuperview];
}];
}
- (void)executeWithCompletionBlock:(void (^)(UIImage *, NSError *, NSDictionary *))completionBlock
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIImage *image = [self buildImage];
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock(image, nil, nil);
});
});
}
#pragma mark-
- (UISlider*)defaultSliderWithWidth:(CGFloat)width
{
UISlider *slider = [[UISlider alloc] initWithFrame:CGRectMake(0, 0, width, 34)];
[slider setMaximumTrackImage:[UIImage new] forState:UIControlStateNormal];
[slider setMinimumTrackImage:[UIImage new] forState:UIControlStateNormal];
[slider setThumbImage:[UIImage new] forState:UIControlStateNormal];
slider.thumbTintColor = [UIColor whiteColor];
return slider;
}
- (UIImage*)colorSliderBackground
{
CGSize size = _colorSlider.frame.size;
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGRect frame = CGRectMake(5, (size.height-10)/2, size.width-10, 5);
CGPathRef path = [UIBezierPath bezierPathWithRoundedRect:frame cornerRadius:5].CGPath;
CGContextAddPath(context, path);
CGContextClip(context);
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
CGFloat components[] = {
0.0f, 0.0f, 0.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 0.0f, 0.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f,
0.0f, 1.0f, 0.0f, 1.0f,
0.0f, 1.0f, 1.0f, 1.0f,
0.0f, 0.0f, 1.0f, 1.0f
};
size_t count = sizeof(components)/ (sizeof(CGFloat)* 4);
CGFloat locations[] = {0.0f, 0.9/3.0, 1/3.0, 1.5/3.0, 2/3.0, 2.5/3.0, 1.0};
CGPoint startPoint = CGPointMake(5, 0);
CGPoint endPoint = CGPointMake(size.width-5, 0);
CGGradientRef gradientRef = CGGradientCreateWithColorComponents(colorSpaceRef, components, locations, count);
CGContextDrawLinearGradient(context, gradientRef, startPoint, endPoint, kCGGradientDrawsAfterEndLocation);
UIImage *tmp = UIGraphicsGetImageFromCurrentImageContext();
CGGradientRelease(gradientRef);
CGColorSpaceRelease(colorSpaceRef);
UIGraphicsEndImageContext();
return tmp;
}
- (UIImage*)widthSliderBackground
{
CGSize size = _widthSlider.frame.size;
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
UIColor *color = [[[CLImageEditorTheme theme] toolbarTextColor] colorWithAlphaComponent:0.5];
CGFloat strRadius = 1;
CGFloat endRadius = size.height/2 * 0.6;
CGPoint strPoint = CGPointMake(strRadius + 5, size.height/2 - 2);
CGPoint endPoint = CGPointMake(size.width-endRadius - 1, strPoint.y);
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddArc(path, NULL, strPoint.x, strPoint.y, strRadius, -M_PI/2, M_PI-M_PI/2, YES);
CGPathAddLineToPoint(path, NULL, endPoint.x, endPoint.y + endRadius);
CGPathAddArc(path, NULL, endPoint.x, endPoint.y, endRadius, M_PI/2, M_PI+M_PI/2, YES);
CGPathAddLineToPoint(path, NULL, strPoint.x, strPoint.y - strRadius);
CGPathCloseSubpath(path);
CGContextAddPath(context, path);
CGContextSetFillColorWithColor(context, color.CGColor);
CGContextFillPath(context);
UIImage *tmp = UIGraphicsGetImageFromCurrentImageContext();
CGPathRelease(path);
UIGraphicsEndImageContext();
return tmp;
}
- (UIColor*)colorForValue:(CGFloat)value
{
if(value<1/3.0){
return [UIColor colorWithWhite:value/0.3 alpha:1];
}
return [UIColor colorWithHue:((value-1/3.0)/0.7)*2/3.0 saturation:1 brightness:1 alpha:1];
}
- (void)setMenu
{
CGFloat W = 70;
_colorSlider = [self defaultSliderWithWidth:_menuView.width - W - 20];
_colorSlider.left = 10;
_colorSlider.top = 5;
[_colorSlider addTarget:self action:@selector(colorSliderDidChange:) forControlEvents:UIControlEventValueChanged];
_colorSlider.backgroundColor = [UIColor colorWithPatternImage:[self colorSliderBackground]];
_colorSlider.value = 0;
[_menuView addSubview:_colorSlider];
_widthSlider = [self defaultSliderWithWidth:_colorSlider.width/2 - 5];
_widthSlider.left = 10;
_widthSlider.top = _colorSlider.bottom + 5;
[_widthSlider addTarget:self action:@selector(widthSliderDidChange:) forControlEvents:UIControlEventValueChanged];
_widthSlider.value = 0.1;
_widthSlider.backgroundColor = [UIColor colorWithPatternImage:[self widthSliderBackground]];
[_menuView addSubview:_widthSlider];
_alphaSlider = [self defaultSliderWithWidth:_colorSlider.width/2 - 5];
_alphaSlider.left = _widthSlider.left + _widthSlider.width + 10;
_alphaSlider.top = _colorSlider.bottom + 5;
[_alphaSlider addTarget:self action:@selector(alphaSliderDidChange:) forControlEvents:UIControlEventValueChanged];
_alphaSlider.value = 0.1;
_alphaSlider.backgroundColor = [UIColor colorWithPatternImage:[self widthSliderBackground]];
[_menuView addSubview:_alphaSlider];
_strokePreview = [[UIView alloc] initWithFrame:CGRectMake(0, 0, W - 5, W - 5)];
_strokePreview.layer.cornerRadius = _strokePreview.height/2;
_strokePreview.layer.borderWidth = 1;
_strokePreview.layer.borderColor = [[[CLImageEditorTheme theme] toolbarTextColor] CGColor];
_strokePreview.center = CGPointMake(_menuView.width-W/2, _menuView.height/2);
[_menuView addSubview:_strokePreview];
_strokePreviewBackground = [[UIView alloc] initWithFrame:_strokePreview.frame];
_strokePreviewBackground.layer.cornerRadius = _strokePreviewBackground.height/2;
_strokePreviewBackground.alpha = 0.3;
[_strokePreviewBackground addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(strokePreviewDidTap:)]];
[_menuView insertSubview:_strokePreviewBackground aboveSubview:_strokePreview];
_eraserIcon = [[UIImageView alloc] initWithFrame:_strokePreview.frame];
_eraserIcon.image = [self imageForKey:kCLDrawToolEraserIconName defaultImageName:@"btn_eraser.png"];
_eraserIcon.hidden = YES;
[_menuView addSubview:_eraserIcon];
[self colorSliderDidChange:_colorSlider];
[self widthSliderDidChange:_widthSlider];
[self alphaSliderDidChange:_alphaSlider];
_menuView.clipsToBounds = NO;
}
- (void)colorSliderDidChange:(UISlider*)sender
{
if(_eraserIcon.hidden){
_strokePreview.backgroundColor = [self colorForValue:_colorSlider.value];
_strokePreviewBackground.backgroundColor = _strokePreview.backgroundColor;
_colorSlider.thumbTintColor = _strokePreview.backgroundColor;
}
}
- (void)widthSliderDidChange:(UISlider*)sender
{
CGFloat scale = MAX(0.05, _widthSlider.value);
_strokePreview.transform = CGAffineTransformMakeScale(scale, scale);
_strokePreview.layer.borderWidth = 2/scale;
}
- (void)alphaSliderDidChange:(UISlider*)sender
{
CGFloat scale = MAX(0.01, _alphaSlider.value);
_strokePreview.alpha = scale;
}
- (void)strokePreviewDidTap:(UITapGestureRecognizer*)sender
{
_eraserIcon.hidden = !_eraserIcon.hidden;
if(_eraserIcon.hidden){
[self colorSliderDidChange:_colorSlider];
}
else{
_strokePreview.backgroundColor = [[CLImageEditorTheme theme] toolbarTextColor];
_strokePreviewBackground.backgroundColor = _strokePreview.backgroundColor;
}
}
- (void)drawingViewDidPan:(UIPanGestureRecognizer*)sender
{
CGPoint currentDraggingPosition = [sender locationInView:_drawingView];
if(sender.state == UIGestureRecognizerStateBegan){
_prevDraggingPosition = currentDraggingPosition;
}
if(sender.state != UIGestureRecognizerStateEnded){
[self drawLine:_prevDraggingPosition to:currentDraggingPosition];
}
if(sender.state == UIGestureRecognizerStateEnded){
UIGraphicsBeginImageContext(_drawingView.frame.size);
[_drawingView.image drawAtPoint:CGPointZero];
if(!_eraserIcon.hidden) {
[_tempView.image drawInRect: CGRectMake(0, 0, _drawingView.width, _drawingView.height)];
} else {
[_tempView.image drawInRect: CGRectMake(0, 0, _drawingView.width, _drawingView.height)
blendMode: kCGBlendModeNormal alpha: _alphaSlider.value];
}
_drawingView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
_tempView.image = nil;
}
_prevDraggingPosition = currentDraggingPosition;
}
-(void)drawLine:(CGPoint)from to:(CGPoint)to
{
CGSize size = _tempView.frame.size;
if(!_eraserIcon.hidden) {
size = _drawingView.frame.size;
}
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
if(!_eraserIcon.hidden) {
[_drawingView.image drawAtPoint:CGPointZero];
} else {
[_tempView.image drawAtPoint:CGPointZero];
}
CGFloat strokeWidth = MAX(1, _widthSlider.value * 65);
CGContextSetLineWidth(context, strokeWidth);
UIColor *strokeColor = _strokePreview.backgroundColor;
CGContextSetStrokeColorWithColor(context, strokeColor.CGColor);
CGContextSetLineCap(context, kCGLineCapRound);
if(!_eraserIcon.hidden) {
CGContextSetBlendMode(context, kCGBlendModeClear);
} else {
CGContextSetBlendMode(context, kCGBlendModeNormal);
}
CGContextMoveToPoint(context, from.x, from.y);
CGContextAddLineToPoint(context, to.x, to.y);
CGContextStrokePath(context);
if(!_eraserIcon.hidden) {
_drawingView.alpha = MAX(0.01, _alphaSlider.value);
_drawingView.image = UIGraphicsGetImageFromCurrentImageContext();
} else {
_tempView.alpha = MAX(0.01, _alphaSlider.value);
_tempView.image = UIGraphicsGetImageFromCurrentImageContext();
}
UIGraphicsEndImageContext();
}
- (UIImage*)buildImage
{
UIGraphicsBeginImageContextWithOptions(_originalImageSize, NO, self.editor.imageView.image.scale);
[self.editor.imageView.image drawAtPoint:CGPointZero];
[_drawingView.image drawInRect:CGRectMake(0, 0, _originalImageSize.width, _originalImageSize.height)];
UIImage *tmp = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return tmp;
}
@end
@yackle Thanks for your suggestions. I updated the code of CLDrawTool. A UIImageView for drawing the current line is added. A Slider for changing the alpha of the current line is added too.
Could you help me to update the CLDrawTool's code online using above code? This maybe benefit other people.
nice work! If you open a pull request I'll be able to merge.
If I set the alpha of the strokeColor when drawing a line, the result will be very stiff and grainy.
Excuse me, is there a better solution to get a smoother and translucent line? Thanks for your attentions.![simulator screen shot](https://cloud.githubusercontent.com/assets/6253031/24078862/2b6d8128-0cb4-11e7-9030-f05ae8b77431.png)