How to draw a transparent stroke (or anyway clear part of an image) on the iPhone
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
and the various CG draw
functions. I primarily draw strokes/lines with the function CGContextAddLineToPoint
Now my issue is this:
The user can draw lines of various colors. I want to give him the ability to use a "rubber"
tool to delete some part of the image drawn so far, with the finger. I initially did this by using a white color for the stroke (set with the CGContextSetRGBStrokeColor
function) but it didn't work out...because I discovered later that the UIImage
on the UIImageView
actually had a transparent background, not white...so I would end up with a transparent image with white lines on it!
Is there anyway to set a "transparent"
stroke color or is there any other way to clear the content of the CGContextRef
under the user's finger, when he moves it?
Thanks
Solution 1:
This will do the trick:
CGContextSetBlendMode(context, kCGBlendModeClear)
Solution 2:
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)...
Solution 3:
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...