how to use setpixel function in iphone
If you're drawing a UIView, I don't believe there is any way to set an individual pixel. Instead, try using CoreGraphics by overriding your UIView's drawRect: method:
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGRect pixelRect;
pixelRect.x = 100; // or whatever position needs a pixel drawn
pixelRect.y = 100;
pixelRect.width = 1;
pixelRect.height = 1;
CGContextSetRGBFillColor(ctx,1,1,1,1.0); // just fill with a white color
CGContextFillRect(ctx, pixelRect);
Another approach may be to consider using OpenGL and just drawing points that way:
glMatrixMode (GL_PROJECTION);
glLoadIdentity ();
glOrtho (0, rect.size.width, rect.size.height, 0, 0, 1); // set to the drawing rectangle's size
glDisable(GL_DEPTH_TEST);
glMatrixMode (GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.375, 0.375, 0);
glClear(GL_COLOR_BUFFER_BIT);
// draw the actual point
glBegin(GL_POINTS);
glColor3f(1.0f, 1.0f, 1.0f); // white color
glVertex2f(x, y);
glEnd();
Related
I have UIView class and in method I want to draw first rectangles and sometimes circle
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
if ([WhatToDraw isEqual:#"Fields"]) {
[self DrawField:context];
}
if ([WhatToDraw isEqual:#"Ball"]) {
[self DrawBall:context x:20 y:20];
}
}
-(void)DrawBall:(CGContextRef)context x:(float) x y:(float) y
{
UIGraphicsPushContext(context);
CGRect rect = CGRectMake(x, y, 25, 25);
CGContextClearRect(context, rect);
CGContextFillEllipseInRect(context, rect);
}
-(void)DrawField:(CGContextRef)context
{
columns = 6;
float offset = 5;
float boardWidth = self.frame.size.width;
float allOffset = (columns + 2) * offset;
float currentX = 10;
float currentWidth = (boardWidth - allOffset) / columns;
float currentHeight = currentWidth;
self.fieldsArray = [[NSMutableArray alloc] init];
//create a new dynamic button board
for (int columnIndex = 0; columnIndex<columns; columnIndex++) {
float currentY = offset;
for (int rowIndex=0; rowIndex<columns; rowIndex++) {
UIGraphicsPushContext(context);
//create new field
CGContextSetRGBFillColor(context, 1.0, 0.0, 0.0, 1.0);
CGContextBeginPath(context);
CGRect rect = CGRectMake(currentX, currentY, currentWidth, currentHeight);
CGContextAddRect(context, rect);
CGContextFillPath(context);
currentY = currentY + offset + currentHeight;
}
currentX = currentX + offset + currentWidth;
}
}
I also have method changing what to draw
-(void)Draw:(NSString*)Thing
{
self.WhatToDraw = Thing;
[self setNeedsDisplay];
}
Drawing rectangles (Fields) is ok, but when I click button to draw circle all rectangles disappear and only circle was drawn.
How can I draw circle on existing rectangle ?
The Problem
Your problem is that when a UIView redraws a region as marked by setNeedsDisplay or setNeedsDisplayInRect it will completely clear that region before executing your drawing code. This means that unless you draw both the rectangles and circle in a single drawing operation within drawRect you will never see the two both drawn in the area you choose to redraw, whether it be the entire view bounds with setNeedsDisplay or a specific area with setNeedsDisplayInRect.
The Solutions
There's no reason why you can't draw both the rectangles and circle each time within drawRect and optimise the performance of the drawing by only redrawing the regions necessary with setNeedsDisplayInRect.
Alternatively you could break up the content using CALayers and have the rectangles in one layer and the circle in another. This would allow you to leverage the animation capabilities of Core Animation. Core animation provides a simple and effective way to manipulate onscreen layers with implicit animations such as moving, resizing, changing colour etc.
my guess, the CGContextClearRect call in your DrawBall method is the responsable of rectangles disappearing... From Apple documentation: If the provided context is a window or bitmap context, Quartz effectively clears the rectangle.
I'm currently drawing a line using Core Graphics. It's really bare bones and simple.
- (void)drawRect:(CGRect)rect {
CGContextRef c = UIGraphicsGetCurrentContext();
CGFloat red[4] = {1.0f, 0.0f, 0.0f, 1.0f};
CGContextSetStrokeColor(c, red);
CGContextBeginPath(c);
CGContextMoveToPoint(c, 5.0f, 5.0f);
CGContextAddLineToPoint(c, 300.0f, 600.0f);
CGContextSetLineWidth(c, 25);
CGContextSetLineCap(c, kCGLineCapRound);
CGContextStrokePath(c);
}
This works well. Let's say that we wanted to draw a custom style line. Say we wanted to imitate the style of a crayon for example. And that the designer handed your crayon style images: http://imgur.com/a/N40ig
To do accomplish this effect I think I need to do something like this:
Create a special colored versions of crayonImage1-crayonImage4
Every time you add a line to line you use one of the crayonImages
You alternate the crayonImages every time you draw a point.
Step 1 makes sense. I can use the following method:
- (UIImage *)image:(UIImage *)img withColor:(UIColor *)color {
// begin a new image context, to draw our colored image onto
UIGraphicsBeginImageContext(img.size);
// get a reference to that context we created
CGContextRef context = UIGraphicsGetCurrentContext();
// set the fill color
[color setFill];
// translate/flip the graphics context (for transforming from CG* coords to UI* coords
CGContextTranslateCTM(context, 0, img.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
// set the blend mode to color burn, and the original image
CGContextSetBlendMode(context, kCGBlendModeColorBurn);
CGRect rect = CGRectMake(0, 0, img.size.width, img.size.height);
CGContextDrawImage(context, rect, img.CGImage);
// set a mask that matches the shape of the image, then draw (color burn) a colored rectangle
CGContextClipToMask(context, rect, img.CGImage);
CGContextAddRect(context, rect);
CGContextDrawPath(context,kCGPathFill);
// generate a new UIImage from the graphics context we drew onto
UIImage *coloredImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//return the color-burned image
return coloredImg;
}
I'm unsure how I can complete steps 2 and 3. Is there an API in CoreGraphics for setting an image as the point of line? If so what is it and how can I use it?
Thanks in advance,
-David
Start with the following example: http://www.ifans.com/forums/showthread.php?t=132024
But for brushes, don't draw a line. Simply draw the brush image using CGContextDrawImage.
Basically, you simply draw an image for every touch.
The gradient in question is Figure 8-5 from the Quartz 2D Programming Guide, "A radial gradient that varies between a point and a circle".
I'm trying to build a CGGradient object (not a CGShading object, which might be the problem) like so:
CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
CGFloat colors[] =
{
0, 0, 0, 0.9,
0, 0, 0, 0
};
CGGradientRef gradient = CGGradientCreateWithColorComponents(rgb, colors, NULL, sizeof(colors)/(sizeof(colors[0])*sizeof(CGFloat)));
CGContextClipToRect(context, rect);
CGContextDrawRadialGradient(context, gradient, startCenter, startRadius, endCenter, endRadius, gradientDrawingOptions);
CGGradientRelease(gradient);
CGColorSpaceRelease(rgb);
Of course, that isn't exactly right -- the centre points and radii are correct, but the actual gradient doesn't look the same. I just wish Apple had provided the source code for each example! >:(
UPDATE: These color values add the shading on top of other content (drawing from a point out to a circle):
CGFloat colors[] =
{
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.75f
};
Using these color values is pretty close (drawing from a point out to a circle):
CGFloat colors[] =
{
0.0f, 1.0f, 1.0f, 1.0f,
0.0f, 0.0f, 0.0f, 0.75f
};
As far as I have understood so far, every time I draw something in the drawRect: of a UIView, the whole context is erased and then redrawn.
So I have to do something like this to draw a series of dots:
Method A: drawing everything on every call
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextDrawImage(context, self.bounds, maskRef); //draw the mask
CGContextClipToMask(context, self.bounds, maskRef); //respect alpha mask
CGContextSetBlendMode(context, kCGBlendModeColorBurn); //set blending mode
for (Drop *drop in myPoints) {
CGContextAddEllipseInRect(context, CGRectMake(drop.point.x - drop.size/2, drop.point.y - drop.size/2, drop.size, drop.size));
}
CGContextSetRGBFillColor(context, 0.5, 0.0, 0.0, 0.8);
CGContextFillPath(context);
}
Which means, I have to store all my Dots (that's fine) and re-draw them all, one by one, every time I want to add a new one. Unfortunately this gives my terrible performance and I am sure there is some other way of doing this, more efficiently.
EDIT: Using MrMage's code I did the following, which unfortunately is just as slow and the color blending doesn't work. Any other method I could try?
Method B: saving the previous draws in a UIImage and only drawing the new stuff and this image
- (void)drawRect:(CGRect)rect
{
//draw on top of the previous stuff
UIGraphicsBeginImageContext(self.frame.size);
CGContextRef ctx = UIGraphicsGetCurrentContext(); // ctx is now the image's context
[cachedImage drawAtPoint:CGPointZero];
if ([myPoints count] > 0)
{
Drop *drop = [myPoints objectAtIndex:[myPoints count]-1];
CGContextClipToMask(ctx, self.bounds, maskRef); //respect alpha mask
CGContextAddEllipseInRect(ctx, CGRectMake(drop.point.x - drop.dropSize/2, drop.point.y - drop.dropSize/2, drop.dropSize, drop.dropSize));
CGContextSetRGBFillColor(ctx, 0.5, 0.0, 0.0, 1.0);
CGContextFillPath(ctx);
}
[cachedImage release];
cachedImage = [UIGraphicsGetImageFromCurrentImageContext() retain];
UIGraphicsEndImageContext();
//draw on the current context
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextDrawImage(context, self.bounds, maskRef); //draw the mask
CGContextSetBlendMode(context, kCGBlendModeColorBurn); //set blending mode
[cachedImage drawAtPoint:CGPointZero]; //draw the cached image
}
EDIT:
After all I combined one of the methods mention below with redrawing only in the new rect. The result is:
FAST METHOD:
- (void)addDotAt:(CGPoint)point
{
if ([myPoints count] < kMaxPoints) {
Drop *drop = [[[Drop alloc] init] autorelease];
drop.point = point;
[myPoints addObject:drop];
[self setNeedsDisplayInRect:CGRectMake(drop.point.x - drop.dropSize/2, drop.point.y - drop.dropSize/2, drop.dropSize, drop.dropSize)]; //redraw
}
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextDrawImage(context, self.bounds, maskRef); //draw the mask
CGContextClipToMask(context, self.bounds, maskRef); //respect alpha mask
CGContextSetBlendMode(context, kCGBlendModeColorBurn); //set blending mode
if ([myPoints count] > 0)
{
Drop *drop = [myPoints objectAtIndex:[myPoints count]-1];
CGPathAddEllipseInRect (dotsPath, NULL, CGRectMake(drop.point.x - drop.dropSize/2, drop.point.y - drop.dropSize/2, drop.dropSize, drop.dropSize));
}
CGContextAddPath(context, dotsPath);
CGContextSetRGBFillColor(context, 0.5, 0.0, 0.0, 1.0);
CGContextFillPath(context);
}
Thanks everyone!
If you are only actually changing a small portion of the UIView's content every time you draw (and the rest of the content generally stays the same), you can use this. Rather than redraw all the content of the UIView every single time, you can mark only the areas of the view that need redrawing using -[UIView setNeedsDisplayInRect:] instead of -[UIView setNeedsDisplay]. You also need to make sure that the graphics content is not cleared before drawing by setting view.clearsContextBeforeDrawing = YES;
Of course, all this also means that your drawRect: implementation needs to respect the rect parameter, which should then be a small subsection of your full view's rect (unless something else dirtied the entire rect), and only draw in that portion.
You can save your CGPath as a member of your class. And use that in the draw method, you will only need to create the path when the dots change but not every time the view is redraw, if the dots are incremental, just keep adding the ellipses to the path. In the drawRect method you will only need to add the path
CGContextAddPath(context,dotsPath);
-(CGMutablePathRef)createPath
{
CGMutablePathRef dotsPath = CGPathCreateMutable();
for (Drop *drop in myPoints) {
CGPathAddEllipseInRect ( dotsPath,NULL,
CGRectMake(drop.point.x - drop.size/2, drop.point.y - drop.size/2, drop.size, drop.size));
}
return dotsPath;
}
If I understand your problem correctly, I would try drawing to a CGBitmapContext instead of the screen directly. Then in the drawRect, draw only the portion of the pre-rendered bitmap that is necessary from the rect parameter.
How many ellipses are you going to draw? In general, Core Graphics should be able to draw a lot of ellipses quickly.
You could, however, cache your old drawings to an image (I don't know if this solution is more performant, however):
UIGraphicsBeginImageContext(self.frame.size);
CGContextRef ctx = UIGraphicsGetCurrentContext(); // ctx is now the image's context
[cachedImage drawAtPoint:CGPointZero];
// only plot new ellipses here...
[cachedImage release];
cachedImage = [UIGraphicsGetImageFromCurrentImageContext() retain];
UIGraphicsEndImageContext();
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextDrawImage(context, self.bounds, maskRef); //draw the mask
CGContextClipToMask(context, self.bounds, maskRef); //respect alpha mask
CGContextSetBlendMode(context, kCGBlendModeColorBurn); //set blending mode
[cachedImage drawAtPoint:CGPointZero];
If you are able to cache the drawing as an image, you can take advantage of UIView's CoreAnimation backing. This will be much faster than using Quartz, as Quartz does its drawing in software.
- (CGImageRef)cachedImage {
/// Draw to an image, return that
}
- (void)refreshCache {
myView.layer.contents = [self cachedImage];
}
- (void)actionThatChangesWhatNeedsToBeDrawn {
[self refreshCache];
}
Does anyone know how to animate a radar animation like the image below?
alt text http://img197.imageshack.us/img197/7124/circle0.png
With it growing outwards? I have to draw the circle using Quartz or some other way?
To draw a static circle in a UIView subclass:
- (void)drawRect:(CGRect)rect
{
CGRect rect = { 0.0f, 0.0f, 100.0f, 100.0f };
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0);
CGContextSetLineWidth(context, 2.0f);
CGContextAddEllipseInRect(context, rect);
CGContextStrokePath(context);
}
From there you just need to animate it with a timer, vary the size of the rect, and add more circles.