How to draw a transparent stroke (or anyway clear part of an image) on the iPhone

后端 未结 6 1562
轻奢々
轻奢々 2020-12-02 07:14

I have a small app that allows the user to draw on the screen with the finger. I have a UIImageView where the user draws, by creating a CGContextRef

相关标签:
6条回答
  • 2020-12-02 07:32
    //erase part
    if(mouseSwiped)
    {
    
    //**************Working Code*************// 
    
    
    UIGraphicsBeginImageContext(frontImage.frame.size);
    [frontImage.image drawInRect:CGRectMake(0, 0, frontImage.frame.size.width, frontImage.frame.size.height)];
    CGContextSetLineCap(UIGraphicsGetCurrentContext(),kCGImageAlphaNone); //kCGImageAlphaPremultipliedLast);
    CGContextSetLineWidth(UIGraphicsGetCurrentContext(), 10);
    CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 1, 0, 0, 10);
    CGContextBeginPath(UIGraphicsGetCurrentContext());
    CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
    CGContextClearRect (UIGraphicsGetCurrentContext(), CGRectMake(lastPoint.x, lastPoint.y, 10, 10));
    CGContextStrokePath(UIGraphicsGetCurrentContext());
    frontImage.image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    
    lastPoint = currentPoint;
    
    mouseMoved++;
    
    if (mouseMoved == 10) 
    {
        mouseMoved = 0;
    }
    }
    
    0 讨论(0)
  • 2020-12-02 07:34

    If you want to erase Image with touch this is my code .... the unerase part not recommended it is working but slightly slow.

    func addNewPathToImage(){
        print("Start erasing or not erasing")
    
        UIGraphicsBeginImageContextWithOptions(TopImageUIImageView.bounds.size, false, UIScreen.main.scale) // or 0 for the scale
    
    //My image is aspect fit so I need to get rect
    
        let aspect = TopImageUIImageView.image!.size.width / TopImageUIImageView.image!.size.height
        let viewRatio = TopImageUIImageView.bounds.size.width / TopImageUIImageView.bounds.size.height
    
    
        if aspect < viewRatio {
            let scale = TopImageUIImageView.bounds.size.height / TopImageUIImageView.image!.size.height
            let width = scale * TopImageUIImageView.image!.size.width
            let topLeftX = (TopImageUIImageView.bounds.size.width - width) * 0.5
            rect = CGRect(x: topLeftX, y: 0, width: width, height: TopImageUIImageView.bounds.size.height)
        }
        else {
            let scale = TopImageUIImageView.bounds.size.width / TopImageUIImageView.image!.size.width
            let height = scale * TopImageUIImageView.image!.size.height
            let topLeftY = (TopImageUIImageView.bounds.size.height - height) * 0.5
            rect = CGRect(x: 0.0, y: topLeftY, width: TopImageUIImageView.bounds.size.width, height: height)
        }
        ////
        context = UIGraphicsGetCurrentContext()
        TopImageUIImageView.image?.draw(in: rect)
        context?.setLineCap(.round)
        context?.setLineWidth(brushSize)
    
    
    
        if isErasing == true {
    
            context?.setShadow(offset: CGSize(width: 0, height: 0), blur: blurNumber)
            context?.setStrokeColor(UIColor.white.cgColor)
            context?.setBlendMode(.clear)
    
    
        }else{
    
            print("test the unerase image .... ?")
            context?.setStrokeColor(UIColor.init(patternImage: topImage.af_imageAspectScaled(toFit: CGSize(width: TopImageUIImageView.bounds.size.width, height: TopImageUIImageView.bounds.size.height))).cgColor)
    
        }
    
    
       // I am using these because I am using touch to define what to erase 
            context?.move(to: CGPoint(x: lastTouch.x, y: lastTouch.y))
            context?.addLine(to: CGPoint(x: currentTouch.x, y: currentTouch.y))
            context?.strokePath()
            context?.closePath() // when add this line or the "context?.beginPath" get ERROR CGContextClosePath: no current point.
            print("close the path")
    
    
    
    
            //get your image 
            let image = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
            TopImageUIImageView.image = image
    
    }
    
    0 讨论(0)
  • 2020-12-02 07:36

    This will do the trick:

    CGContextSetBlendMode(context, kCGBlendModeClear)
    
    0 讨论(0)
  • 2020-12-02 07:43

    I ended up using Bresenham's line algorithm (harkening back to the days of yore when I had to write my own graphics routines)...

    - (void) contextEraseLine:(CGContextRef) ctx from:(CGPoint)startPoint to:(CGPoint) endPoint withThickness:(int)thickness {
        int x, cx, deltax, xstep,
        y, cy, deltay, ystep,
        error, st, dupe;
    
        int x0, y0, x1, y1;
    
        x0 = startPoint.x;
        y0 = startPoint.y;
        x1 = endPoint.x;
        y1 = endPoint.y;
    
        // find largest delta for pixel steps
        st = (abs(y1 - y0) > abs(x1 - x0));
    
        // if deltay > deltax then swap x,y
        if (st) {
            (x0 ^= y0); (y0 ^= x0); (x0 ^= y0); // swap(x0, y0);
            (x1 ^= y1); (y1 ^= x1); (x1 ^= y1); // swap(x1, y1);
        }
    
        deltax = abs(x1 - x0);
        deltay = abs(y1 - y0);
        error  = (deltax / 2);
        y = y0;
    
        if (x0 > x1) { xstep = -1; }
        else         { xstep =  1; }
    
        if (y0 > y1) { ystep = -1; }
        else         { ystep =  1; }
    
        for ((x = x0); (x != (x1 + xstep)); (x += xstep))
        {
            (cx = x); (cy = y); // copy of x, copy of y
    
            // if x,y swapped above, swap them back now
            if (st) { (cx ^= cy); (cy ^= cx); (cx ^= cy); }
    
            (dupe = 0); // initialize no dupe
    
            if(!dupe) { // if not a dupe, write it out
                //NSLog(@"(%2d, %2d)", cx, cy);
    
                CGContextClearRect(ctx, CGRectMake(cx, cy, thickness, thickness));
    
        }
    
            (error -= deltay); // converge toward end of line
    
            if (error < 0) { // not done yet
                (y += ystep);
                (error += deltax);
            }
        }
    }
    

    Phew! That's a long way to go to create a (somewhat) clunky eraser line.

    To use it, do something like:

    - (void)eraseStart {
        // erase lines
        UIGraphicsBeginImageContext(drawingBoard.size);
        ctx = UIGraphicsGetCurrentContext();
        CGContextDrawImage(ctx,CGRectMake(0,0,drawingBoard.size.width, drawingBoard.size.height),[drawingBoard CGImage]); 
    }
    
    - (void)eraseEnd {
        drawingBoard = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
    
        [drawingView removeFromSuperview];
        [drawingView release];
    
        drawingView = [[UIImageView alloc] initWithImage:drawingBoard];
        drawingView.frame = CGRectMake(intEtchX, intEtchY, intEtchWidth, intEtchHeight);
    
        [self.view addSubview:drawingView];
    }
    

    This assumes you have already created a drawingView (UIImageView) and drawingBoard (UIImage).

    Then, to erase a line, simply do something like:

    CGContextRef ctx = UIGraphicsGetCurrentContext();
    [self eraseStart];
    [self contextEraseLine:ctx from:CGPointMake (x1, y1) to:CGPointMake (x2, y2) withThickness:10];
    [self eraseEnd];
    

    (replace x1, y1, x2, and y2 with appropriate values)...

    0 讨论(0)
  • 2020-12-02 07:47

    I've tried using:

    CGContextSetStrokeColorWithColor (myContext, [[UIColor clearColor] CGColor]);
    

    but that doesn't work, because it seems to be "drawing" on top of the context with an invisible color (and invisible color + whatever color it is drawing on = the color it's drawing on).

    The only solution I've found (which isn't optimal) is:

    CGContextClearRect (myContext, CGRectMake(x, y, width, height));
    

    Unfortunately, that means you have to trace a series of rects and generate the line yourself...

    0 讨论(0)
  • 2020-12-02 07:54
    UIColor *clearColor = [UIColor clearColor];
    
    0 讨论(0)
提交回复
热议问题