Non-destructive Drawing

Sparrow's SPRenderTexture allows you to do nondestructive drawing on a bitmapped style canvas. You can use different images with transparency to create interesting brushes.

To do this you will want to create 2x SPImages. 1 with your brush texture and 1 with your canvas. The canvas will need to use an SPRenderTexture as it's backing. You will then want to create an a touch handler and an enter frame handler to process the touch events, since render textures can get messed with if you call them directly from touch handlers.

Once you have all this in place you will start logging the touches when the user touches. These logged points can then be parsed in your enter frame handler where you do the actual drawing on the SPRenderTexture. You may want to create extra points between your new touch and last touch so that you get a nice line if your touches are far apart from each other.

One thing that is super handy in Sparrow is the ability to change the color of an image. There is a tutorial here. You can use this to change out the color of your brushes.

There is lots of room for experimentation here. Since the canvas never gets destroyed you can do things like create particles to follow your touches drawing lines everywhere or use extra brushes to create things like drips.

#import <Foundation/Foundation.h>
#import "Sparrow.h"
 
#define numSteps 6
 
@interface SparrowPainting : SPStage{
	CGPoint lastTouch;
	CGPoint newTouch;
	SPImage *brush;
	SPRenderTexture *canvasTexture;
	SPImage *canvasImage;
	BOOL drawing;
}
 
- (void)update:(SPEnterFrameEvent*)event;
- (void)touched:(SPTouchEvent*)event;
- (void)changeBrushColor:(uint)brushColor;
 
@end
#import "SparrowPainting.h" 
 
@implementation SparrowPainting
 
- (id)initWithWidth:(float)width height:(float)height {
	self = [super initWithWidth:width height:height];
 
	// create a brush to draw with
	brush = [SPImage imageWithContentsOfFile:@"brush.png"];
	[brush retain];
 
	// create the render texture to draw on
	canvasTexture = [[SPRenderTexture alloc] initWithWidth:320 height:480];
	[canvasTexture clearWithColor:0x000000 alpha:1];
	canvasImage = [SPImage imageWithTexture:canvasTexture];
	[self addChild:canvasImage];
 
	// change the color of the brush
	[self changeBrushColor:0xFF0000];
 
	// add event listeners
	[self addEventListener:@selector(update:) atObject:self forType:SP_EVENT_TYPE_ENTER_FRAME];
	[self addEventListener:@selector(touched:) atObject:self forType:SP_EVENT_TYPE_TOUCH];
 
    return self;
}
 
 
- (void)update:(SPEnterFrameEvent*)event{
 
	if(drawing){
		// group the draw calls together for speed
		[canvasTexture bundleDrawCalls:^{
			double incX = (newTouch.x - lastTouch.x)/numSteps;
			double incY = (newTouch.y - lastTouch.y)/numSteps;
			brush.x = lastTouch.x - (brush.width/2);
			brush.y = lastTouch.y - (brush.height/2);
			// loop through so that if our touches are far apart we still create a line
			for (int i=0; i<numSteps; i++){
				[canvasTexture drawObject:brush];
				brush.x += incX;
				brush.y += incY;
 
			}
		}];
		lastTouch = CGPointMake(newTouch.x, newTouch.y);
	}
}
 
- (void)touched:(SPTouchEvent*)event{
	SPTouch *touchStart = [[event touchesWithTarget:self andPhase:SPTouchPhaseBegan] anyObject];
	SPPoint *touchPosition;
    if(touchStart){
        touchPosition = [touchStart locationInSpace:self];
		lastTouch = CGPointMake(touchPosition.x, touchPosition.y);
		newTouch = CGPointMake(touchPosition.x, touchPosition.y);
    }
 
	SPTouch *touchMove = [[event touchesWithTarget:self andPhase:SPTouchPhaseMoved] anyObject];
	if(touchMove){
		touchPosition = [touchMove locationInSpace:self];
		newTouch = CGPointMake(touchPosition.x, touchPosition.y);
		drawing = YES;
	}
 
	SPTouch *touchEnd = [[event touchesWithTarget:self andPhase:SPTouchPhaseEnded] anyObject];
    if(touchEnd){
		touchPosition = [touchEnd locationInSpace:self];
		lastTouch = CGPointMake(touchPosition.x, touchPosition.y);
		newTouch = CGPointMake(touchPosition.x, touchPosition.y);
		drawing = NO;
    }
}
 
- (void)changeBrushColor:(uint)brushColor{
	// changes the brush color based on the pixels in the image
	brush.color = brushColor;
}
 
- (void)dealloc{
	// cleanup
	[self removeEventListener:@selector(update:) atObject:self forType:SP_EVENT_TYPE_ENTER_FRAME];
	[self removeEventListener:@selector(touched:) atObject:self forType:SP_EVENT_TYPE_TOUCH];
 
	[brush release];
	[canvasTexture release];
	[self removeChild:canvasImage];
 
	[super dealloc];
}
 
@end
  tutorials/non_destructive_drawing.txt · Last modified: 2013/03/05 10:19 (external edit)
 
Powered by DokuWiki