Como desenhar um traço transparente (ou parte de qualquer maneira clara de uma imagem) no iPhone

StackOverflow https://stackoverflow.com/questions/629409

  •  08-07-2019
  •  | 
  •  

Pergunta

Eu tenho um pequeno aplicativo que permite ao usuário desenhar na tela com o dedo. Eu tenho um UIImageView onde o usuário desenha, criando um CGContextRef e as várias funções CG draw. Chamo principalmente traços / linhas com a CGContextAddLineToPoint função

Agora o meu problema é o seguinte: O usuário pode desenhar linhas de várias cores. Eu quero dar-lhe a capacidade de usar uma ferramenta "rubber" para apagar uma parte da imagem desenhada até agora, com o dedo. Inicialmente eu fiz isso usando uma cor branca para o acidente vascular cerebral (definido com a função CGContextSetRGBStrokeColor), mas não deu certo ... porque eu descobri mais tarde que o UIImage na UIImageView realmente tinha um fundo transparente, não branco ... então eu iria acabar com uma imagem transparente com linhas brancas sobre ele!

Existe uma maneira de definir uma cor de traço "transparent" ou existe alguma outra maneira de limpar o conteúdo da CGContextRef sob o dedo do usuário, quando ele se move-lo? Graças

Foi útil?

Solução

Isto irá fazer o truque:

CGContextSetBlendMode(context, kCGBlendModeClear)

Outras dicas

Acabei usando Algoritmo de Bresenham (remetendo para os dias de outrora, quando eu tive que escrever minhas próprias rotinas 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);
        }
    }
}

Ufa! Isso é um longo caminho a percorrer para criar uma (um pouco) linha de borracha desajeitado.

Para usá-lo, fazer 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];
}

Isso pressupõe que você já tiver criado um drawingView (UIImageView) e DrawingBoard (UIImage).

Então, para apagar uma linha, basta fazer algo como:

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

(substitua x1, y1, x2 e y2 com valores apropriados) ...

Eu tentei usar:

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

mas que não funciona, porque parece ser "desenho" no topo do contexto com uma cor invisível (e cor invisível + qualquer cor que ele está a aproveitar a = a cor de seus desenhos em).

A única solução que eu encontrei (o que não é o ideal) é:

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

Infelizmente, isso significa que você tem que traçar uma série de retângulos e gerar a linha-se ...

Se você quiser apagar Imagem com toque este é meu código .... a parte unerase não recomendado que está trabalhando, mas um pouco 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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top