画笔-新方案2 数据模型配置 将线条属性移至DrawPageData模型中。 1 2 3 4 5 @interface DrawPageData : NSObject @property (nonatomic ,strong ) NSMutableArray *nowPointArray; @property (nonatomic ,strong ) NSMutableArray *lineColorsArray;@property (nonatomic ,strong ) NSMutableArray *linePointArray; @end
CLDrawTool 中 新增两属性用来记录所有图层以及图层所对应的划线数据模型。 1 2 @property (nonatomic ,strong ) NSMutableArray *drawImageViews; @property (nonatomic ,strong ) NSMutableArray *drawPages;
撤销: 1 2 3 _lastDrawPage.linePointArray removeLastObject 移除最后一条线; _lastDrawPage.lineColorsArray removeLastObject 移除颜色; clearTopDrawImageView 移除最顶层的画笔图片层。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 @property (nonatomic ,strong ) DrawPageData _lastDrawPage; ### 画笔撤销事件 -(void )eraserButtonDidTap:(UIButton *)btn{ if (_lastDrawPage.linePointArray && _lastDrawPage.linePointArray.count >= 1 ) { [_lastDrawPage.linePointArray removeLastObject]; [_lastDrawPage.lineColorsArray removeLastObject]; [self drawAllPanLine:_drawingView byDrawPage:_lastDrawPage]; if (_lastDrawPage.linePointArray.count == 0 ){ if (self .drawImageViews.count > 1 ){ [self clearTopDrawImageView]; } } }else { if (self .drawPages.count > 1 ){ [self clearTopDrawImageView]; [self eraserButtonDidTap:nil ]; } } } -(void )clearTopDrawImageView{ [self .drawPages removeLastObject]; [((UIImageView *)self .drawImageViews.lastObject) removeFromSuperview]; [self .drawImageViews removeLastObject]; _lastDrawPage = self .drawPages.lastObject; _drawingView = self .drawImageViews.lastObject; }
画笔图层渲染
现在的渲染逻辑是每个 DrawPage 里面的 linePointArray 只会有一条线,外层 for 循环其实可以省略。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 -(void )drawAllPanLine:(UIImageView *)drawImageView byDrawPage:(DrawPageData *)lastDrawPage { CGSize size = drawImageView.frame.size; UIGraphicsBeginImageContextWithOptions (size, NO , 0.0 ); for (int i = 0 ; i < lastDrawPage.linePointArray.count ; i ++ ) { NSMutableArray *pointArray = [lastDrawPage.linePointArray objectAtIndex:i]; CGContextRef context = UIGraphicsGetCurrentContext (); CGFloat strokeWidth = 6.5 ; CGContextSetLineWidth (context, strokeWidth); CGContextSetLineJoin (context, kCGLineJoinRound); CGContextSetLineCap (context, kCGLineCapRound); UIColor *strokeColor = lastDrawPage.lineColorsArray[i]; CGContextSetStrokeColorWithColor (context, strokeColor.CGColor); for (int j = 0 ; j < pointArray.count ; j ++ ) { NSValue *value = [pointArray objectAtIndex:j]; CGPoint p = [value CGPointValue ]; if (j == 0 ) { CGContextMoveToPoint (context, p.x, p.y); CGContextAddLineToPoint (context, p.x, p.y); }else { CGContextAddLineToPoint (context, p.x, p.y); } } CGContextStrokePath (context); } drawImageView.image = UIGraphicsGetImageFromCurrentImageContext (); UIGraphicsEndImageContext (); }
Pan 手势的事件处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 - (void )drawingViewDidPan:(UIPanGestureRecognizer *)sender { if ([[self .editor currentEditTool] isKindOfClass:[CLMosaicTool class ]]){ CLMosaicTool *mosaicTool = [self .editor.usedToolDic objectForKey:@"CLMosaicTool" ]; [mosaicTool drawingViewDidPan:sender]; return ; } CGPoint currentDraggingPosition = [sender locationInView:_drawingView]; if (sender.state == UIGestureRecognizerStateBegan ){ _lastDrawPage.nowPointArray = [[NSMutableArray alloc] init]; UIColor *currentLineStrokeColor = _strokePreview.backgroundColor; [_lastDrawPage.lineColorsArray addObject:currentLineStrokeColor]; [_lastDrawPage.linePointArray addObject:_lastDrawPage.nowPointArray]; _prevDraggingPosition = currentDraggingPosition; } NSValue *point = [NSValue valueWithCGPoint:currentDraggingPosition]; [_lastDrawPage.nowPointArray addObject:point]; [self drawAllPanLine:_drawingView byDrawPage:_lastDrawPage]; _prevDraggingPosition = currentDraggingPosition; if (sender.state == UIGestureRecognizerStateEnded ){ [self generateDrawImageView]; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 -(void )generateDrawImageView{ DrawPageData *pageData = [[DrawPageData alloc] init]; [self .drawPages addObject:pageData]; _lastDrawPage = pageData; UIImageView *lastDrawImageView = [[UIImageView alloc] initWithFrame:self .editor.imageView.bounds]; UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector (drawingViewDidPan:)]; panGesture.maximumNumberOfTouches = 1 ; lastDrawImageView.userInteractionEnabled = YES ; lastDrawImageView.tag = 100 + self .drawImageViews.count; [lastDrawImageView addGestureRecognizer:panGesture]; if (self .drawImageViews.count){ [self .editor.imageView insertSubview:lastDrawImageView aboveSubview:_drawingView]; }else { [self .editor.imageView addSubview:lastDrawImageView]; } [self .drawImageViews addObject:lastDrawImageView]; _drawingView = lastDrawImageView; }
画笔图层图片旋转 以下代码在CLClipping旋转或裁剪操作完成时候的回调需要对涂鸦图层做的image和frame处理。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 CLDrawTool *_drawTool = [self .editor.usedToolDic objectForKey:@"CLDrawTool" ];if (_drawTool){ for (UIImageView *drawingView in _drawTool.drawImageViews){ UIImage *roateDrawImage = [self buildRoteImage:drawingView.image]; CGFloat roateZoomScale = _rotateImageView.width / roateDrawImage.size.width; CGRect roateRct = _gridView.clippingRect; roateRct.size.width /= roateZoomScale; roateRct.size.height /= roateZoomScale; roateRct.origin.x /= roateZoomScale; roateRct.origin.y /= roateZoomScale; UIImage *roateCropDrawImage = [roateDrawImage crop:roateRct]; drawingView.image = roateCropDrawImage; [self .editor resetDrawImageViewFrame:roateCropDrawImage withDrawImageView:drawingView]; } }
马赛克-新方案 撤销的操作(新) 新方案的撤销是和画笔流程解决方案一致,都是每完成一次触摸涂抹马赛克,则在表面覆盖一张图片.
在MosaicPageData 数据源基础上新增两个layer属性。 CAShapeLayer shapeLayer; CALayer imageLayer;
操作原理:通过设置当前PageData的 shapeLayer.path 来实现路径涂抹。然后将imageLayer渲染到画布上[lastMosaicPage.imageLayer renderInContext:context];得到image赋值给当前上层的drawImageView.
buildImage:图片合成。 先将原图绘制,然后将每一张马赛克图层的image进行绘制。最后得到合成的马赛克图片。具体参照CLMosialTool buildImage方法。
//准备好原图的马赛克虚化图片。 1 2 3 4 5 6 7 8 9 10 11 12 13 _mohuImage = [XRGBTool getMosaicImageWith:image level:0 ]; _shapeLayer = [CAShapeLayer layer]; _shapeLayer.frame = _drawingView.bounds; _shapeLayer.lineCap = kCALineCapRound; _shapeLayer.lineJoin = kCALineJoinRound; _shapeLayer.lineWidth = 15 ; _shapeLayer.strokeColor = [UIColor blueColor].CGColor; _shapeLayer.fillColor = nil ; _imageLayer = [CALayer layer]; _imageLayer.frame = _drawingView.bounds; _imageLayer.contents = (id )_mohuImage.CGImage; _imageLayer.mask = _shapeLayer;
Pan 手势的事件处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 - (void )drawingViewDidPan:(UIPanGestureRecognizer *)sender { CGPoint currentDraggingPosition = [sender locationInView:_drawingView]; if (sender.state == UIGestureRecognizerStateBegan ){ _prevDraggingPosition = currentDraggingPosition; _lastMosaicPage.nowPointArray = [[NSMutableArray alloc] init]; [_lastMosaicPage.linePointArray addObject:_lastMosaicPage.nowPointArray]; } NSValue *point = [NSValue valueWithCGPoint:currentDraggingPosition]; [_lastMosaicPage.nowPointArray addObject:point]; [self drawAllPanLine:_drawingView byDrawPage:_lastMosaicPage]; _prevDraggingPosition = currentDraggingPosition; if (sender.state == UIGestureRecognizerStateEnded ){ [self generateDrawImageView]; } } -(void )generateDrawImageView{ MosaicPageData *pageData = [[MosaicPageData alloc] init]; [self .mosaicPages addObject:pageData]; _lastMosaicPage = pageData; UIImage *_mohuImage = [XRGBTool getMosaicImageWith:self .editor.imageView.image level:0 ]; UIImageView *drawingView = [[UIImageView alloc] initWithFrame:self .editor.imageView.bounds]; UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector (drawingViewDidPan:)]; panGesture.maximumNumberOfTouches = 1 ; drawingView.userInteractionEnabled = YES ; drawingView.tag = 1000 + self .drawImageViews.count; [drawingView addGestureRecognizer:panGesture]; if (self .drawImageViews.count){ [self .editor.imageView.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { NSLog (@"子imageview的层次tag:%d" ,obj.tag); }]; [self .editor.imageView insertSubview:drawingView aboveSubview:_drawingView]; }else { [self .editor.imageView insertSubview:drawingView atIndex:0 ]; } [self .drawImageViews addObject:drawingView]; _drawingView = drawingView; CALayer *imageLayer = [CALayer layer]; imageLayer.frame = drawingView.bounds; imageLayer.contents = (id )_mohuImage.CGImage; CAShapeLayer *shapeLayer = [CAShapeLayer layer]; shapeLayer.frame = drawingView.bounds; shapeLayer.lineCap = kCALineCapRound; shapeLayer.lineJoin = kCALineJoinRound; shapeLayer.lineWidth = 15 ; shapeLayer.strokeColor = [UIColor blueColor].CGColor; shapeLayer.fillColor = nil ; imageLayer.mask = shapeLayer; _lastMosaicPage.shapeLayer = shapeLayer; _lastMosaicPage.imageLayer = imageLayer; }
马赛克图层渲染 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 -(void )drawAllPanLine:(UIImageView *)drawImageView byDrawPage:(MosaicPageData *)lastMosaicPage { CGMutablePathRef _path = CGPathCreateMutable (); for (int i = 0 ; i < lastMosaicPage.linePointArray.count ; i ++ ) { NSMutableArray *pointArray = [lastMosaicPage.linePointArray objectAtIndex:i]; CGMutablePathRef _onePath = CGPathCreateMutable (); for (int j = 0 ; j < pointArray.count ; j ++ ) { NSValue *value = [pointArray objectAtIndex:j]; CGPoint p = [value CGPointValue ]; if (j == 0 ) { CGPathMoveToPoint (_onePath, nil , p.x, p.y); }else { CGPathAddLineToPoint (_onePath, nil , p.x, p.y); } } CGPathAddPath (_path, nil , _onePath); } lastMosaicPage.shapeLayer.path = _path; CGFloat scale = self .editor.imageView.image.size.width / self .editor.imageView.width; UIGraphicsBeginImageContextWithOptions (self .editor.imageView.image.size, NO , self .editor.imageView.image.scale); CGContextRef context = UIGraphicsGetCurrentContext (); CGContextScaleCTM (UIGraphicsGetCurrentContext (), scale, scale); [lastMosaicPage.imageLayer renderInContext:context]; UIImage *tmp = UIGraphicsGetImageFromCurrentImageContext (); drawImageView.image = tmp; UIGraphicsEndImageContext (); } ### 画笔撤销事件 ```objectivec -(void )eraserButtonDidTap:(UIButton *)btn{ if (_lastMosaicPage.linePointArray && _lastMosaicPage.linePointArray.count >= 1 ) { [_lastMosaicPage.linePointArray removeLastObject]; [self drawAllPanLine:_drawingView byDrawPage:_lastMosaicPage]; if (_lastMosaicPage.linePointArray.count == 0 ){ if (self .drawImageViews.count > 1 ){ [self clearTopDrawImageView]; } } }else { if (self .mosaicPages.count > 1 ){ [self clearTopDrawImageView]; [self eraserButtonDidTap:nil ]; } } } -(void )clearTopDrawImageView{ [self .mosaicPages removeLastObject]; [((UIImageView *)self .drawImageViews.lastObject) removeFromSuperview]; [self .drawImageViews removeLastObject]; _lastMosaicPage = self .mosaicPages.lastObject; _drawingView = self .drawImageViews.lastObject; } - (UIImage *)buildImage { CGSize originalImageSize = self .editor.imageView.image.size; UIGraphicsBeginImageContextWithOptions (originalImageSize, NO , self .editor.imageView.image.scale); [self .editor.imageView.image drawAtPoint:CGPointZero ]; for (UIImageView *eachDrawImageView in self .drawImageViews){ if (eachDrawImageView.image){ [eachDrawImageView.image drawInRect:CGRectMake (0 , 0 , originalImageSize.width, originalImageSize.height)]; } } UIImage *tmp = UIGraphicsGetImageFromCurrentImageContext (); UIGraphicsEndImageContext (); return tmp; }
文字帖view容器旋转 对workingView 容器进行旋转,设置其transform. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 - (void )resetTextWorkingViewFrame:(UIView *)workingView byResultImage:(UIImage *)image transform:(CGAffineTransform )transform { CGRect originFrame = workingView.frame; CLTextTool *textTool = [self .usedToolDic objectForKey:@"CLTextTool" ]; CGFloat offsetBottom = textTool.workingViewBottomOffset; originFrame.size.height += offsetBottom; workingView.frame = originFrame; _textToolNeedAddBottomFlag = NO ; if (offsetBottom == 0 ){ _textToolNeedAddBottomFlag = YES ; } CGSize size = image ? image.size : workingView.frame.size; CGAffineTransform resultTransform = transform; if (!CGAffineTransformIsIdentity (workingView.transform)){ resultTransform = CGAffineTransformConcat (workingView.transform,transform); } workingView.transform = resultTransform; CGRect workingViewWillFrame = workingView.frame; if (size.width>0 && size.height>0 ){ CGFloat ratio = MIN(_scrollView.frame.size.width / size.width, _scrollView.frame.size.height / size.height); CGFloat W = ratio * size.width * _scrollView.zoomScale; CGFloat H = ratio * size.height * _scrollView.zoomScale; CGFloat scaleX = W/workingViewWillFrame.size.width; CGFloat scaleY = H/(workingViewWillFrame.size.height); CGFloat willScale = MIN(scaleX, scaleY); workingView.transform =CGAffineTransformIdentity ; workingView.transform = CGAffineTransformScale (resultTransform, willScale, willScale); }