views:

1377

answers:

3

It is quite hard to tell so I upload an image to show my problem: http://i42.tinypic.com/2eezamo.jpg

Basically in drawRect, I will draw the line from touchesMoved as finger touches and I will call "needsDisplayInRect" for redraw. But I found that the first line is done, the second line will clear the rect part, so some previouse drawing is gone.

Here is my implementation:

enter code here

-(void) drawRect:(CGRect)rect{
//[super drawRect: rect];
CGContextRef context = UIGraphicsGetCurrentContext();
[self drawSquiggle:squiggle at:rect inContext:context];
}

- (void)drawSquiggle:(Squiggle *)squiggle at:(CGRect) rect inContext:(CGContextRef)context
{   

CGContextSetBlendMode(context, kCGBlendModeMultiply);
UIColor *squiggleColor = squiggle.strokeColor; // get squiggle's color
CGColorRef colorRef = [squiggleColor CGColor]; // get the CGColor
CGContextSetStrokeColorWithColor(context, colorRef);        

NSMutableArray *points = [squiggle points]; // get points from squiggle
// retrieve the NSValue object and store the value in firstPoint
CGPoint firstPoint; // declare a CGPoint
[[points objectAtIndex:0] getValue:&firstPoint];
// move to the point
CGContextMoveToPoint(context, firstPoint.x, firstPoint.y);
// draw a line from each point to the next in order

for (int i = 1; i < [points count]; i++)
{
    NSValue *value = [points objectAtIndex:i]; // get the next value
    CGPoint point; // declare a new point
    [value getValue:&point]; // store the value in point

    // draw a line to the new point
    CGContextAddLineToPoint(context, point.x, point.y);
} // end for
CGContextStrokePath(context); 
}
+2  A: 

The graphics context returned by UIGraphicsGetCurrentContext in drawRect: is owned by the system and cleared at the start of a drawing pass. Create your own CGContext to record your drawing and render it to the drawRect: context as needed.

Create a paired CGBitmapContext and CGImage that refer to the same CGDataProvider and have the same size, color space and other properties. Draw into that context when and how you see fit, then in drawRect: use CGContextDrawImage to display it.

Or if you are just doing a series of lines, you can build a CGMutablePath and render it to the system context.

Edit:

Look at CGImageCreate and CGBitmapContextCreate in their respective header files. Most of the arguments are the same. The bitmap context expects a raw data pointer, while the image expects a CGDataProvider. Here is a rough sketch of what the code would look like. In the end you can draw into the context then use the image to render into another context.

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

There are a lot of blanks to fill in, but this should get you started. You could use malloc instead of CFData for the buffer.

drawnonward
I guess I've got your idea. But I am not sure about CGDataProvider and is there example code for that?
snakewa
I have an implementation but it doesn't work as my expected. I guess my understanding is still on the wrong way? I have posted the code would you mind checking it?
snakewa
A: 

I try to implement this but seems it doesn't work as my expected. I have implemented a view to draw an square every time setNeedsDisplay is called on the view. I put CGImageRef image and CGBitmapContextRef context into ivar and allocate them when the view inits.

#import "View.h"


@implementation View


#define bitsPerComponent 8
#define bitsPerPixel 4*bitsPerComponent
#define bytesPerPixel 4


- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
    lastPoint = CGPointMake(50, 50);
    size_t width = frame.size.width;
    size_t height = frame.size.height;

    CFMutableDataRef data = CFDataCreateMutable( NULL , width * height * bytesPerPixel );  
    CGDataProviderRef provider = CGDataProviderCreateWithCFData( data );
    CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();

    /*CGImageRef - ivar */ image = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, width*bytesPerPixel, colorspace, kCGImageAlphaPremultipliedLast, provider, NULL, NO, kCGRenderingIntentDefault);
    /*CGBitmapContextRef - ivar */ context = CGBitmapContextCreate(CFDataGetMutableBytePtr(data), width, height, bitsPerComponent, width*bytesPerPixel , colorspace, kCGImageAlphaPremultipliedLast);

    CFRelease( data ); // retained by provider I think
    CGDataProviderRelease( provider ); // retained by image
    CGColorSpaceRelease(colorspace);
}
return self;
}

- (void)drawRect:(CGRect)rect {
NSLog(@"test");
CGContextSetFillColorWithColor(context, [[UIColor blueColor] CGColor]);
CGContextFillRect(context, CGRectMake(lastPoint.x, lastPoint.y, 100, 100));
lastPoint = CGPointMake( ((int)lastPoint.x+50) % 380 , ((int)lastPoint.y+50) % 220 );
CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, 100, 100),image);
}

- (void)dealloc {
[super dealloc];
}


@end
snakewa
A: 

snakewa, did you ever figure this out?

Mark A.