Simple iPhone drawing app with Quartz 2D - iphone

I am making a simple iPhone drawing program as a personal side-project.
I capture touches event in a subclassed UIView and render the actual stuff to a seperate CGLayer. After each render, I call [self setNeedsLayout] and in the drawRect: method I draw the CGLayer to the screen context.
This all works great and performs decently for drawing rectangles. However, I just want a simple "freehand" mode like a lot of other iPhone applications have.
The way I thought to do this was to create a CGMutablePath, and simply:
CGMutablePathRef path;
-(void)touchBegan {
path = CGMutablePathCreate();
}
-(void)touchMoved {
CGPathMoveToPoint(path,NULL,x,y);
CGPathAddLineToPoint(path,NULL,x,y);
}
-(void)drawRect:(CGContextRef)context {
CGContextBeginPath(context);
CGContextAddPath(context,path);
CGContextStrokePath(context);
}
However, after drawing for more than 1 second, performance degrades miserably.
I would just draw each line into the off-screen CGLayer, if it were not for variable opacity! The less-than-100% opacity causes dots to be left on the screen connecting the lines. I have looked at CGContextSetBlendingMode() but alas I cannot find an answer.
Can anyone point me in the right direction? Other iPhone apps are able to do this with very good efficiency.

The problem is that with CGStrokePath() the current mutable path gets closed and drawn and a new path is created when you move your finger. So you probably end up with a lot of paths for one touch "session", at least that's what your pseudocode seems to do.
You can try to begin a new mutable path when touches begin, use CGAddLineToPoint() when the touches move und end the path when touches end (much like your pseudocode shows). But in the draw method, you draw a copy of the current mutable path, and the actual mutable path is still being elongated until the touches end, so you only get one path for the whole touch session. After the touches end you can add the path permanently - you can for example put all paths into an array and iterate over them in the draw method.

What SanHolo said - plus you may want to throttle the adding of points, so it only adds a new point no more often than every 10ms, say (you'd need to play with the interval). You can do that with a simple timer.
Also, how are you instructing the view that it needs to redraw itself? You might want to throttle that too - and it could be on a longer interval than the point capturing (e.g. capture points no more than every 10ms, and redraw no more often than every 200ms - again you'd need to play with the numbers).
In both cases you'd need to make sure that, if nothing happens for longer than the interval the last point is captured, or the redraw is requested. That's where the timer comes in.

Related

gtkmm3 drawings outside on_draw

I am working on the real-time plot application where a stream of data is to be plotted on screen. Earlier using gtkmm2 I had done this using a custom widget (derived from Gtk::Bin) where I have a member function which creates a cairo context and does the plotting.
Now with gtkmm3 I am unable to plot in any method other than on_draw. Here's what my custom draw method body looks like
Gtk::Allocation oAllocation = get_allocation();
Glib::RefPtr <Gdk::Window> refWindow = get_window();
Cairo::RefPtr <Cairo::Context> refContext =
refWindow->create_cairo_context();
refWindow->begin_paint_rect(oAllocation); //added later
refContext->save();
refContext->reset_clip();
refContext->set_source_rgba(1,
1,
1,
1);
refContext->move_to(oAllocation.get_x(),
oAllocation.get_y());
refContext->line_to(oAllocation.get_x()
+ oAllocation.get_width(),
oAllocation.get_y()
+ oAllocation.get_height());
refContext->stroke();
refContext->restore();
refWindow->end_paint();
Initially I derived the class from Gtk::DrawingArea then tried with Gtk::Bin while adding the begin_paint_rect call.
Is it forbidden to draw in any place other than on_draw?
For something like a plot (or anything that is rather complex to draw) I advise to use a buffer; I lost a month of my life because I read that gtkmm3 does buffering so that using "double buffering" isn't needed anymore (as opposed to gtkmm2), but it aint that simple (read: that isn't true).
So, what you should do is just draw to your own surface; and every time you change something call queue_draw_region or queue_draw_area.
Then in on_draw get the list of clip rectangles and copy those from your private surface to the cr that is passed to the on_draw function. Cairo normally does
the exact same thing (or so they claim), copying what you just copied again, to the screen; so you should turn that off (this should be possible I read).
The reason you can't use Cairo's buffering is because it doesn't KEEP that buffer; what you get is some corrupted surface, so you are forced to redraw EVERYTHING inside the clip rectangle list. That wouldn't be too bad if you (your application) was the only one making changes (as per your queue_draw_* calls): then you could set a flag, invalidate the part(s) that needs redrawing and simply postpone the draw until you get to on_draw. But sometimes on_draw is called for other reasons, for example, when you open a menu that goes over your drawing area. I think this is a bug (or a design error) but it is the way it is. The result is that you can't know what you have to redraw EXCEPT by looking at the clip rectangle list; which makes it incredibly hard to just draw a part of your area unless your drawing is made up of many separate rectangles (like, say, a chess board). The only feasible way is to keep a full copy of the image in memory (your private surface) and just copy the clip rectangle list from there when in on_draw.
Is it forbidden to draw in any place other than on_draw?
Basically: Yes.
The idea is that you call gtk_widget_queue_draw() or gtk_widget_queue_draw_area() when you want to cause a redraw.
https://developer.gnome.org/gtk3/stable/GtkWidget.html#gtk-widget-queue-draw
https://developer.gnome.org/gtk3/stable/GtkWidget.html#gtk-widget-queue-draw-area

How to avoid FPS drop when drawing lines in SpriteKit?

My current project contains a gravity simulator where sprites move in accordance with the forces they experience in the game scene.
One of my features involve allowing moving sprites to draw a line behind them so you can see what paths they take.
Shown here:
However, as the Sprite continues it's movements around the screen, the FPS begins to dive. This can be seen in this second image where some time has passed since the sprite first started its movement.
When researching, I found other people had posted with similar problems:
Multiple skshapenode in one draw?
However, in the question above, the answer's poster detailed that it (The answer) was meant for a static image, which isn't something I want, because this line will change in real time depending on what influences the sprites path, this was reflected when I tried implementing a function to add a new Line to the old one which didn't work. That Code here
I'm asking if anyone can assist me in finding a way to properly stop this constant FPS drop that comes from all the draw operations. My current draw code consists of two Functions.
-(void)updateDrawPath:(CGPoint)a B:(CGPoint)b
{
CGPathAddLineToPoint(_lineToDraw, NULL, b.x, b.y);
_lineNode.path = _lineToDraw;
}
-(void)traceObject:(SKPlanetNode *)p
{
_lineToDraw = CGPathCreateMutable();
CGPathMoveToPoint((_lineToDraw), NULL, p.position.x, p.position.y);
_lineNode = [SKShapeNode node];
_lineNode.path = _lineToDraw;
_lineNode.strokeColor = [SKColor whiteColor];
_lineNode.antialiased = YES;
_lineNode.lineWidth = 3;
[self addChild:_lineNode];
}
updateDrawPath: Draws line to latest position of Sprite.
traceObject: Takes SKPlanetNode (Subclass of SKSpriteNode), and sets it up to have a line drawn after it.
If anyone can suggest a way to do this and also reduce the terrible overhead I keep accumulating, it would be fantastic!
A couple suggestions:
Consider that SKShapeNode is more or less just a tool for debug drawing mostly, due to the fact that it doesn't draw in batches it's really not suitable to make a game around that or to use it extensively (both many shapes as well as few but complex shapes).
You could draw lines using a custom shader which will likely be faster and more elegant solution, though of course you may have to learn how to write shader programs first.
Be sure to measure performance only on a device, never the simulator.

App with expanding animated line in iOS ... howto

The basic idea
is very easy. Simplified you could say... a snake like line realized by a let's say 3px line is expanding across the screen collecting and interacting with different stuff, can be steered through user input. Like a continous line you would draw with a pen.
I've already started reading apple's documentation on Quartz/CG.
As I understand now, I need to put my rendering code into a UIView's drawRect.
Then I would need to set a timer (found some answers/posts here and there, nothing concrete though) that fires x times per second and calls setNeedsDisplay on the UIView.
Question:
How to realize the following:
Have whole snake on UIView 1 / (Layer ?), draw new part on UIView 2, merge them, so that the new part gets appended to UIView 1 (or CALayer instead of views ?). I ask explicitly about this cause I read, that one shouldn't redraw the same content over and over again, but just the new/moving part.
I hope you can provide me some sample code or some details which classes I should use and the strategy on how to use them / which calls to make.
Edit
OK, I see that my idea or what I've read before to realize this with Quartz and different views is not so wise... ( Daniel Bleisteiner )
So I switched to OpenGL now, well I'm looking into it, reading examples, Jeff LaMarche's OpenGL blog entries, etc..
I guess I would be able to draw my line. I think I would create classes for curves, straight lines / direction changes, etc. and then on user input I would create the related objects (depending on the steer input) store them in an array and then recreate and redraw the whole line by reading the object properties from the objects stored in the array on each frame. A simple line I would draw like this (code from Beginning iPhone Development)
glDisable(GL_TEXTURE_2D);
GLfloat vertices[4];
// Convert coordinates
vertices[0] = start.x;
vertices[1] = start.y;
vertices[2] = end.x;
vertices[3] = end.y;
glLineWidth(3.0);
glVertexPointer (2, GL_FLOAT , 0, vertices);
glDrawArrays (GL_LINES, 0, 2);
Maybe I will even find a way to antialias it but
now I'm more curious if my idea is good enough or if there are some better established strategies for this
and maybe someone could tell me how to seperate code for hud's, the line drawing itself and some menus that I will have to display f.e. at the beginning ... so that it's easy to transition from one "view" to another (like with a fade)? Some hints ?
Maybe you have also read a book, that will explain how to solve this kind of problems?
Or you can point me to another good answer / example code as I have huge problems in finding "animated drawing" examples.
this got me a little further, but it is still a little vague for me.
I don't know how to realize "update path that you draw (only with needed points + one last point that moves)"
Don't try to merge different views... setNeedsDisplay has a rect parameter that tells the core graphics part that only a certain part of the screen needs to be rendered again. Respect this parameter in your drawRect method and it should be enough for standard 2D games and tools.
If you intent to use intense graphics there is no other option than to use OpenGL. The performance is multiple times better and you won't have to care about optimizations... OpenGL does much in the background.
Use OpenGL ES. Then what you will want to do is create a run loop function or method which gets called by a CADisplayLink 30 to 60 times per second. 60 is the maximum.
So how does this work?
1) Create an assign-property for CADisplayLink. Do NOT retain your CADisplayLink, because display links and timers retain their target. Otherwise you would create a retain-cycle which may cause abandoned memory issues (which is even worse than a leak and much harder to discover).
#property (nonatomic, assign) CADisplayLink *displayLink;
2) Create and schedule the CADisplayLink:
- (void)startRunloop {
if (!animating) {
CADisplayLink *dl = [[UIScreen mainScreen] displayLinkWithTarget:self selector:#selector(drawFrame)];
[dl setFrameInterval:1];
[dl addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
self.displayLink = dl;
animating = YES;
}
}
Some things to note here: -setFrameInterval:1 tells the CADisplayLink to not skip any frame. This means: You get the maximum fps. BUT: This can be bad if your code needs longer than 1/60 seconds. In that case, it's better to set this to 2, for example. Makes your animation more fluid.
3) In your -drawFrame method do your OpenGL ES drawing as usual. The only difference is that this code gets called multiple times per second. Just keep track of time and determine in your code what you want to draw, and how you want it to be drawn. If you were to animate a rectangle moving from bottom left to top right with an animation duration of 1 second, you would simply interpolate the animation frames between start and end by applying a function which takes time t as argument. There are thousands of ways to do it. This is one of them.
4) When you're done or want to halt OpenGL ES drawing, just invalidate or pause your CADisplayLink. Something like this:
- (void)stopRunloop {
if (animating) {
[self.displayLink invalidate];
self.displayLink = nil;
animating = NO;
}
}

Touch draw in Quatz 2D/Core Graphics

I'm trying to implement "hand draw tool".
At the moment algorythm looks like that (I don't insert any code because methods are quite big, will try to explain an idea):
Drawing
In touchesStarted: method I create NSMutableArray *pointsArray and add point into it. Call setNeedsDisplay: method.
In touchesMoved: method I calculate points between last added point from the pointsArray and current point. Add all points to the pointsArray. Call setNeedsDisplay: method.
In touchesFinished: event I calculate points between last added point from the array and current point. Set flag touchesWereFinished. Call setNeedsDisplay:.
Render:
drawRect: method checks is pointsArray != nil and is there any data in it. If there is - it starts to traw circles in each point of this array. If flag touchesWereFinished is set - save current context to the UIImage, release pointsArray, set it to nil and reset the flag.
There are a lot disadvantages of this method:
It is slow
It becomes extremely slow when user touches and move finger for long time. Array becomes enormous
"Lines" composed by circles are ugly
I would like to change my algorithm to make it bit faster and line smoother.
In result I would like to have lines like on the picture at following URL (sorry, not enough reputation to insert an image): http://2.bp.blogspot.com/_r5VzEAUYXJ4/SrOYp8tJCPI/AAAAAAAAAMw/ZwDKXiHlhV0/s320/SketchBook+Mobile(4).png
Can you advice me, ho I can draw lines this way (smooth and slim on the edges)? I thought to draw circles with alpha gradient on the edges (to make lines smoother), but it will be extremely slowly IMHO.
Thanks for help
Update
I changed draw algorithm. Now every event I save UITouch and in the drawRect: method I draw path from prev. point to the current one. And dump UIImage from context every drawRect: invocation.
But I still have 2 questions:
Is it possible to draw more smooth. I mean if I draw quite fast, I easily can see that path path is a set of straight lines. But I want to draw curves, to make path smooth. Probably Bezier curves will help but I don't understand how they can help in this.
situation.
I want to start path with thiner line and finish with thiner line too. How I can do this line transform?
Thanks!

How to animate line-drawing in iPhone development?

I have been searching around, but there seems no good answer for this simple question. So I am asking again: how to animate line-drawing in iphone dev?
Basically what I want is something like this:
#implementation MyUIView
- (void) triggerLineDrawing: (CGPathRef) path {
...
// animate line drawing here
// and the line should disappear automatically after a few seconds
}
Can it be done?
You can't do it automatically, only bu hand. To do it manually you should do something like this:
create array of your line points.
start an NSTimer, that will fire for example, 15 times a second.
every tick of a timer you have to find out, what part of the line you need to draw (look at linear interpolation)
update path that you draw (only with needed points + one last point that moves)
send setNeedsDraw message to view.
You can vary interpolation algorythm, speed of line drawing etc. to get the effect that you need.