开发者

Draw rectangle using Core Graphics with live preview

I'm creating a simple drawing application and would like some help with drawing rectangles based on the user's touch events. I'm able to draw a rectangle using Core Graphics from the point in touchesBegan to the point in touchesEnded. This rectangle is then displayed once the touchesEnded event is fired.

However, I would like to be able to do this in a 'live' fashion. Meaning, the rectangle is updated and displayed as the user drags their finger. Is this possible? Any help would be greatly appreciated, thank you!

UPDATE:

I was able to get this to work by using the following code. It works perfectly fine for small images, however it get's very slow for large images. I realize my solution is hugely inefficient and was wondering if anyone could suggest a better solution. Thanks.

    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
开发者_运维技巧        UITouch *touch = [touches anyObject];
        _firstPoint = [touch locationInView:self.view];
        _firstPoint.y -= 40;
        _lastPoint = _firstPoint;
    }

    - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
    {
        UITouch *touch = [touches anyObject];
        CGPoint currentPoint = [touch locationInView:self.view];
        currentPoint.y -= 40;

        [self drawSquareFrom:_firstPoint to:currentPoint];

        _lastPoint = currentPoint;
    }

    - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
    {
        UITouch *touch = [touches anyObject];
        CGPoint currentPoint = [touch locationInView:self.view];
        currentPoint.y -= 40;

        [self drawSquareFrom:_firstPoint to:currentPoint];

        [_modifiedImage release];
        _modifiedImage = [[UIImage alloc] initWithCGImage:_imageView.image.CGImage];
    }

    - (void)drawSquareFrom:(CGPoint)firstPoint to:(CGPoint)lastPoint
    {
        _imageView.image = _modifiedImage;

        CGFloat width  = lastPoint.x - firstPoint.x;
        CGFloat height = lastPoint.y - firstPoint.y;

        _path = [UIBezierPath bezierPathWithRect:CGRectMake(firstPoint.x, firstPoint.y, width, height)];

        UIGraphicsBeginImageContext( _originalImage.size );
        [_imageView.image drawInRect:CGRectMake(0, 0, _originalImage.size.width, _originalImage.size.height)];

        _path.lineWidth = 10;
        [[UIColor redColor] setStroke];
        [[UIColor clearColor] setFill];
        [_path fill];
        [_path stroke];

        _imageView.image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
    }


What you are doing right now is creating a paired CGImage and CGBitmapContext for every call to drawSquareFrom:to: and disposing them each time. Instead, create a single paired CGImage and CGBitmapContext that you reuse for each call.

-(void) drawSquareFrom:(CGPoint)from to:(CGPoint)to {
    CGContextRef context = [self offscreenContext];
    CGRect draw , full = [self offscreenContextBounds];

    draw.origin = from;
    draw.size.width = to.x - from.x;
    draw.size.height = to.y - from.y;
    draw = CGRectStandardize( draw );

    [[UIColor redColor] setStroke];
    [[UIColor clearColor] setFill];
    CGContextClearRect( context , full );
    CGContextFillRect( context , draw );
    CGContextStrokeRectWithWidth( context , draw , 10 );
    [_imageView setNeedsDisplay];
}

You create a paired CGImage and CGContext like this, although there are some ... to fill in. All the myX variables are intended to be class members.

-(CGContextRef) offscreenContext {
    if ( nil == myContext ) {
        size_t width = 400;
        size_t height = 300;
        CFMutableDataRef data = CFDataCreateMutable( NULL , width * height * 4 ); // 4 is bytes per pixel
        CGDataProviderRef provider = CGDataProviderCreateWithCFData( data );
        CGImageRef image = CGImageCreate( width , height , ... , provider , ... );
        CGBitmapContextRef context = CGBitmapContextCreate( CFDataGetMutableBytePtr( data ) , width , height , ... );
        CFRelease( data ); // retained by provider I think
        CGDataProviderRelease( provider ); // retained by image

        myImage = image;
        myContext = context;
        myContextFrame.origin = CGPointZero;
        myContextFrame.size.width = width;
        myContextFrame.size.height = height;
        _imageView.image = [UIImage imageWithCGImage:image];
    }

    return myContext;
}
-(CGRect) offscreenContextBounds {
    return myContextFrame;
}

This sets the _imageView.image to the newly created image. In drawSquareFrom:to: I assume that setNeedsDisplay is sufficient to draw the changes to made, but it may be that you need to assign a new UIImage each time that wraps the same CGImage.


Just use two Contexts. In one you just "preview" draw the Rectangle and you can clear it on TouchesMoved. On TouchesEnd you finally draw on your original context and then on the Image.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜