Pregunta

Tengo una pequeña aplicación que permite al usuario dibujar en la pantalla con el dedo. Tengo un UIImageView donde el usuario dibuja, creando un CGContextRef y las diversas funciones CG draw . Principalmente dibujo trazos / líneas con la función CGContextAddLineToPoint

Ahora mi problema es este: El usuario puede dibujar líneas de varios colores. Quiero darle la posibilidad de usar una herramienta " rubber " para eliminar una parte de la imagen dibujada hasta ahora, con el dedo. Inicialmente hice esto usando un color blanco para el trazo (configurado con la función CGContextSetRGBStrokeColor ) pero no funcionó ... porque descubrí más tarde que el UIImage en el UIImageView en realidad tenía un fondo transparente, no blanco ... ¡así que terminaría con una imagen transparente con líneas blancas!

¿Hay alguna forma de establecer un color de trazo " transparente " o hay alguna otra forma de borrar el contenido del CGContextRef bajo el dedo del usuario, cuando se mueve ¿eso? Gracias

¿Fue útil?

Solución

Esto hará el truco:

CGContextSetBlendMode(context, kCGBlendModeClear)

Otros consejos

Terminé usando el algoritmo de línea de Bresenham (recordando los días de antaño cuando tenía que escribir mis propias rutinas gráficas) ...

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

¡Uf! Es un largo camino por recorrer para crear una línea de borrador (algo) torpe.

Para usarlo, haga algo como:

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

Esto supone que ya ha creado un drawingView (UIImageView) y drawingBoard (UIImage).

Luego, para borrar una línea, simplemente haga algo como:

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

(reemplace x1, y1, x2 e y2 con los valores apropiados) ...

He intentado usar:

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

pero eso no funciona, porque parece estar "dibujando" encima del contexto con un color invisible (y color invisible + cualquier color en el que se dibuje = el color en el que se dibuja).

La única solución que he encontrado (que no es óptima) es:

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

Desafortunadamente, eso significa que tienes que rastrear una serie de rectificaciones y generar la línea tú mismo ...

Si desea borrar la imagen con un toque, este es mi código ... la parte de borrar no se recomienda, pero funciona un poco lento.

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

}
//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;
}
}
UIColor *clearColor = [UIColor clearColor];
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top