I needed to create a small drawing/paint application, so i turned to Beginning IPad Development for IPhone Developers: Mastering the IPad SDK By Jack Nutting, Dave Wooldridge, David Mark.
It's pretty nice. The architecture is strong. But, the drawing application (Dudel) is not very good in terms of performance. There are two main issues (for me, at the moment):
Most Important: The drawing slows down after a while. Reason: drawRect is being called, every time, for all the paths.
The drawing with Pencil tool isn't smooth. Reason: It's using addLineToPoint: instead of (may be) addQuadCurveToPoint:.
There's no Eraser control. But it's really not an issue, because we can choose white color for the painting go give an illusion of an eraser. But, if there's a better implementation for that, i'm interested to know.
Question:
Is there a solution out there that addresses these issues, and provide a simple but efficient drawing application?
Note: I need the Undo/Redo feature as well.
Let me try to answer your question one by one.
The drawing with Pencil tool isn't smooth. Reason: It's using addLineToPoint: instead of (may be) addQuadCurveToPoint:.
What you say is true.IOS device looks for touches in a defined interval. If you move your hand fast, it is very likely that you may end up losing few touch points . Hence connecting points using lines results in ugly spikes.
We can smooth the curve by curve fitting algorithms.However Some tweaks in the drawing can result in measurable improvement in quality. Here is an example for it.
Eraser control
If you know the background color you can use that as brush color to give an eraser feeling. if you do not know the background you can use
CGContextSetBlendMode(context, kCGBlendModeClear);
Undo/Redo
you can use NSUndoManager .
Related
I've written an iOS app in which I'm using CGLayer quite successfully. While researching ways to squeeze a bit more performance out of this app, I came across this blog post: http://iosptl.com/posts/cglayer-no-longer-recommended/ in which the author very broadly states that CGLayer is to never be used. An individual post alone is not cause for concern, but I've also found people referring to this post as something to abide by.
No real specifics are offered. For instance, the author states that "sometimes it's faster, sometimes it's slower". This makes me wonder if the concern is that, in general, programmers will not use this object correctly.
I suppose this question is for the seasoned Cocoa/Cocoa Touch developers. Is there any merit to this? Is CGLayer indeed something to avoid and if so, are there specific, measurable reasons as to why?
To start with my answer, I would like to conclude that its totally your own design decision that whether you would use CGLayer in your app or not.
The real thing is that if you are drawing something on-screen, it will possibly buy you nothing on iOS platform. On iOS, the basic screen composition block is a CALayer. CALayer takes a Quartz(CG) graphics context to draw that on screen and that might be a context created by CGLayer itself. Now, CALayer being hardware accelerated by itself, would try to cache any graphics content to graphics card and reuse them. And that's the purpose what we had used a CGLayer previously.
Also, if offscreen rendering is concerned, a CALayer can do that when shouldRasterize is set to YES and under some other circumstances. However, keep in mind that offscreen composition is again another task performed by CPU before handing over the rendered content to GPU. So again there is no clear winner.
CGLayer would be particularly handy when creating a CG context that wont be drawn on screen, like a PDF context.
I'm not sure why Apple development team has asked one to avoid CGLayer completely. Might be there are some underlying architectural flaw but that is undocumented, till date. However, until we are sure about that and we have existing apps designed over CGLayer architecture, I dont find any specific reason to completely abandon that.
For me the most relevant point is what the author wrote in one of his follow up comments:
As I understand it from the Core Graphics team, they basically haven’t touched it [CGLayer] since before the iPhone came out. It was one of those things that sounded really good, but didn’t work out in practice. But it’s not actually broken, so there’s no reason to deprecate it. And as I mentioned, if you have awesome CGLayer code, I don’t see any reason to replace it. CGLayer isn’t bad. It’s just not maintained like other parts of CG.
It would be helpful if Apple's Quartz 2D Programming Guide (updated 2014) didn't contain the following prominent comment box:
Note: Bitmap graphics contexts are sometimes used for drawing offscreen. Before you decide to use a bitmap graphics context for this purpose, see Core Graphics Layer Drawing. CGLayer objects (CGLayerRef) are optimized for offscreen drawing because, whenever possible, Quartz caches layers on the video card.
No. Ignore the "Never" cited in the blog, unless you never profile the impact CGLayer has on your app.
Consider CGLayers as a potential optimization for your program. CGLayers have the potential to affect your program's performance and resource consumption in positive and negative ways (a tradeoff in many cases). In the abstract, it's much like a cache (which have their own costs). Alternative caching mechanisms have their own associated costs, and CGLayer may or may not be the best caching implementation for your program.
New user to the site, but I have used it in the past so I felt it best to ask my question here, for the best chance of getting a response.
What I'm dealing with is one object, this being the sprite for my latest app, which I need to check for when it comes in to contact with another object, in this case, a tunnel which will curve.
Now, I'm aware of CGRectIntersectsRect, however I can't see that being helpful, as if I've got 2 UIImages, that being the top and bottom of a "mountain", and said pieces curving, there's no doubt that the sprite would touch the "rectangle".
What I need is something to trigger when the sprite hits the actual wall, however my limited knowledge of Objective-C isn't helping my case.
I imagine someone out there will know what I can do to resolve this, as for all I know it could be a simple solution.
Thank you in advance everyone!
First, I'd probably not build these basic pieces yourself. For iOS 7, you can use SpriteKit, which is built-in. If you want to support older versions of iOS, look at cocos2d (it's good for iOS 7, too).
But to the question, one approach for detecting arbitrary overlaps is to draw both objects into a buffer and check if there are any overlapping pixels (for instance, by drawing one in in pure red, and another in pure green, and then looking for pixels that have both). For a discussion of how to do this kind of thing in Core Graphics, see Clipping a CGRect to a CGPath, which provides sample code for the simplest version (checking for the intersection of a rectangle and curve), but the same approach can be used more generally. Note that this drawing can get expensive if you're doing it constantly, so usually you first check whether the bounding rectangles overlap. That tells you whether it's even worth the trouble to look closer.
But first I'd look at SpriteKit.
I need to add typical finger drawing to an app.
(The usual.... choose colors, erase, thickness - just the usual you see in every app made.)
It's hard to believe I have to program this from scratch, in this day and age?
It's hard to believe there is not a common solution for this?
All I could find is...
https://github.com/levinunnink/Smooth-Line-View
Review: that is a (beautifully written) example fragment of a few lines of code being how to draw with a path, with a good smooth result. It works perfectly, but it's just for drawing one "path." For example, you can't make a drawing with more than one color. there is no undo or anything like that. (See also note on the fourth review item here regarding path-math.)
https://github.com/sumanthk2006/Smooth-Line-View-1 (variation of above, but does not run under ARC, unfortunately demo does not properly launch etc)
Review: this is "abandonedware" or maybe "frustrationware" :) It does not run and is not ARC-ready. It is a seemingly well-written class that claims to do undo, colors, erase, etc. It even has a proper delegate to track your button states. But it doesn't work.
UPDATE Regarding this paackage. As a matter of fact, if you massage it it works well. Suggest (i) throw away the example app files. (ii) using modern Xcode, use the "convert to ARC project" feature on the two main files. There are a coupe little problems like it should use awakeFromNib. If you get it working it actually does everything, really well.
THERE ARE SERIOUS BUGS in SmoothLineView1. just to be clear, it suffers some serious bugs - you'll see there's a "streaking" effect when you draw.
http://www.cdframeworks.com/product/brushengine
Review: This commercial package (under $100) is well made and email support is fast. Unfortunately it does not have undo so it's not suitable for many situations. (Also does not have erase.)
Mentioned below is this popular article:
http://mobile.tutsplus.com/tutorials/iphone/ios-sdk_freehand-drawing/
Review: this is a good article on the actual TECHNOLOGY of drawing curves, on the let's say "path-math". Unfortunately it's no help at all if you need a working, ready to use, drawing package (with the obvious features, undo, erase, colors etc) to use in an iOS app. Thus for example, whatever actual solution you were using, you may, perhaps, want to apply the math concepts in this article.
I appreciate that the basic concepts are very simple, it's easy enough to "start from scratch". But it's just - ridiculous - to have to do from scratch for something so commonplace. Is there a solid package I'm missing?
It's fairly amazing that the four scratchy references above are the only things out there.
Any ideas? What's the best package today (late 2013) for adding drawing to an iOS app? THANKS.
As of 2017, I use https://github.com/acerbetti/ACEDrawingView/.
It can be used through cocoa pods, and provide full functionality over integrating a drawing feature in your app (color, brush, size, eraser, undo/redo/, etc).
http://mobile.tutsplus.com/tutorials/iphone/ios-sdk_freehand-drawing/ has a good, clear tutorial on smooth freehand drawing on the ios. If you just follow the tutorial, you can implement it easily into whatever app you are trying to make. It has stuff about increasing line quality and smoothness, and stroke quality.http://www.raywenderlich.com/18840/ is another tutorial on a simple drawing app, self-contained. You can combine aspects of both to get what you want.
edit: https://developer.apple.com/library/ios/samplecode/GLPaint/Introduction/Intro.html is the GLPaint sample provided by apple, which includes detecting shake. I thought it seemed more official, since it's from apple
to answer your question, there is no package for drawing directly, but there is a UIKit class, UIBezierPath, that lets you draw shapes made of straight lines or some curves.
I'm working on an paint app for iOS platform.And I used CGContextAddQuadCurveToPoint to make the line more smooth. But I'm stuck in how to simulate the stroke.
Papers 53 is a really cool app. I just wanna simulate the stroke just like what 'Papers 53' does.
Any idea about changing the width of the line dynamically and smoothly during drawing?
I found an article about this stuff.
http://www.merowing.info/2012/04/drawing-smooth-lines-with-cocos2d-ios-inspired-by-paper/
This one is really reliable and I just used its algorithm to simulate smooth stroke by using Quartz2d successfully.
I have a custom view (inherited from UIView) in my app. The custom view overrides
- (void) drawRect:(CGRect) rect
The problem is: the drawRect: executes many times longer on iPad 3 than on iPad 2 (about 0.1 second on iPad 3 and 0.003 second on iPad 2). It's about 30 times slower.
Basically, I am using some pre-created layers and draw them in the drawRect:. The last call
CGContextDrawLayerAtPoint(context, CGPointZero, m_currentLayer);
takes most of the time (about 95% of total time in drawRect:)
What might be slowing things so much and how should I fix the cause?
UPDATE:
There are no threads directly involved. I do call setNeedsDisplay: in one thread and drawRect: gets called from another but that's it. The same goes for locks (there are no locks used).
The view gets redrawn in response to touches (it's a coloring book app). On iPad 2 I get reasonable delay between a touch and an update of the screen. I want to achieve the same on iPad 3.
So, the iPad 3 is definitely slower in a lot of areas. I have a theory about this. Marco Arment noted that the method renderInContext is ridiculously slow on the new iPad. I also found this to be the case when trying to create a magnifying glass for a custom text view. In the end I had to forego renderInContext for custom Core Graphics drawing.
I've also been having problem hitting the dreaded wait_fences errors on my core graphics drawing here: Only on new iPad 3: wait_fences: failed to receive reply: 10004003.
This is what I've figured out so far. The iPad 3 obviously has 4 times the pixels to drive. This can cause problems in two place:
First, the CPU. All core graphics drawing is done by the CPU. In the case of rotational events, if the CPU takes too long to draw, it hits the wait_fences error, which I believe is simply a call that tells the device to wait a little longer to actually perform the rotation, thus the delay.
Transferring images to the GPU. The GPU obviously handles the retina resolution just fine (see Infinity Blade 2). But when core graphics draws, it draws its images directly to the GPU buffers to avoid memcpy. However, either the GPU buffers haven't changes since the iPad 2 or they just didn't make them large enough, because it's remarkably easy to overload those buffers. When that happens, I believe the CPU writes the images to standard memory and then copies them to the GPU when the GPU buffers can handle it. This, I think is what causes the performance problems. That extra copy is time consuming with so many pixels and slows things down considerably.
To avoid memcpy I recommend several things:
Only draw what you need. Avoid drawing anything offscreen at all costs. If you're drawing a large view, but only display part of that view (subviews covering it, for example) try to find a way to only draw what is visible.
If you have to draw a large view, consider breaking the view up in to parts either as subviews or sublayers (probably sublayers in your case). And only redraw what you need. Take the notability app, for example. When you zoom in, you can literally watch it redraw one square at a time. Or in safari you can watch it update squares as you scroll. Unfortunately, I haven't had to do this so I'm uncertain of the methodology.
Try to keep your drawings simple. I had an awesome looking custom core text view that had to redraw on every character entered. Very slow. I changed the background to simple white (in core graphics) and it sped up well. Even better would be for me to not redraw the background.
I would like to point out that my theory is conjecture. Apple doesn't really explain what exactly they do. My theory is just based on what they have said and how the iPad responds as well as my own experimentation.
UPDATE
So Apple has now released the 2012 WWDC Developer videos. They have two videos that may help you (requires developer account):
iOS App Performance: Responsiveness
iOS App Performance: Graphics and Animation
One thing they talk about I think may help you is using the method: setNeedsDisplayInRect:(CGRect)rect. Using this method instead of the normal setNeedsDisplay and making sure that your drawRect method only draws the rect given to it can greatly help performance. Personally, I use the function: CGContextClipToRect(context, rect); to clip my drawing only to the rect provided.
As an example, I have a separate class I use to draw text directly to my views using Core Text. My UIView subclass keeps a reference to this object and uses it to draw it's text rather than use a UILabel. I used to refresh the entire view (setNeedsDisplay) when the text change. Now I have my CoreText object calculate the changed CGRect and use setNeedsDisplayInRect to only change the portion of the view that contains the text. This really helped my performance when scrolling.
I ended up using approach described in #Kurt Revis answer for similar question.
I minimized number of layers used, added UIImageView and set its image to an UIImage wrapping my CGImageRef. Please read the mentioned answer to get more details about the approach.
In the end my application become even simpler than before and works with almost identical speed on iPad 2 and iPad 3.