iOS: draw RGBA data in drawRect without prior conversion to UIImage - ios

I have an image stored in a RGBA buffer. Now I want to draw this data in the UIView's drawRect method:
- (void)drawRect:(CGRect)rect
{
// draw RGBA here
}
One means would be to copy the data into an UIImage (outside the drawRect method) and draw this UIImage. Is there a method to directly draw the data without having to make another copy in an UIImage?
Regards,

Using the drawrect function of a UIView for this purpose would be horrendously slow. Any time you animate the view, move it, move something else on top of it or whatever it would have to be redrawn (slow process). UIImageView is optimized for this task and it would be beneficial to use it.
But if you are dead set on drawrect then you would have to create "pixel" rectangles by taking the width and height of your UIView divided by the width and height of your image (in pixels). Then iterate through every pixel row and column and use the CGContextSetFillColor and CGContextFillRect. It would probably make performance sense to sort the array of colors into like colors and draw the same color all at once. But still much slower than UIImageView. The memory overlap of having your array and a UIImageView is not bad enough to outweigh the beneifits.

Related

Performance UIImageView vs UIView with QuartzCore

So I just discovered QuartzCore, and I am now considering to replace a UIImageView containing a bitmap with a UIView subclass doing things like
CGContextFillEllipseInRect(contextRef, rect);
They would look exactly the same: the bitmap is just a little filled circle.
The very view I'm replacing is playing an important role in my app: it's being dragged around a lot.
Question: performance wise: should I bother? I can imagine that the vector circle is being recalculated all of the time, while the bitmap is just buffered. Or that the vector is easier to digest than a bitmap.
Can anyone advise?
thanks ahead
All UIView's on iOS are layer backed. So drawRect will only be called once and you will draw to the CALayer backing the view. You can have it draw again by calling setNeedsDisplay. When you are dragging the view around and drawing it, the view will render from the layer backing. Using a UIImageView is also layer backed and so the end result should be two layer backed views. The one place where you may see a difference is in low memory situations when the view is not visible (though I am not sure).

How to animate size of custom drawn text?

I'm working on a educational app involving complex scripts in which I paint parts of different 'letters' different colours. UILabel is out of the question, so I've drilled down into Core Text and am having a surprisingly successful go of painting glyphs in CALayers.
What I haven't managed to do is animate the size of my custom drawn text. Basically I have text on 'tiles' (CALayers) that move around the screen. The moving around is okay, but now I want to zoom in on the ones that users press.
My idea is to try to cache a 'full resolution' tile and then draw it to scale during an animation of an image bounds. So far I've tried to draw and cache and then redraw such a tile in the following way:
UIGraphicsBeginImageContext(CGSizeMake(50, 50));
CGContextRef context = UIGraphicsGetCurrentContext();
//do some drawing...
myTextImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Then in [CALayer drawInContext:(CGContextRef)context],
I call [myTextImage drawAtPoint:CGPointZero].
When I run the app, the console shows <Error>: CGContextDrawImage: invalid context 0x0. Meanwhile I can perfectly while just continue to draw text in context in the same method even after that error is logged.
So I have two questions: (1) Why isn't this working? Should I be using CGBitmap instead?
And more important: (2) Is there a smarter way of solving the overall problem? Maybe storing my text as paths and then somehow getting CAAnimation to draw it at different scales as the bounds of the enclosing CALayer change?
Okay, this is much easier than I thought. Simply draw the text in the drawInContext: of a CALayer inside of a UIView. Then animate the view using the transform property, and the text will shrink or expand as you like.
Just pay attention to scaling so that the text doesn't get blocky. The easiest way to do that is to make sure the transform scale factors do not go above 1. In other words, make the 'default' 1:1 size of your UIView the largest size you ever want to display it.

On iPhone and iPad, can we draw anything without using drawRect?

It seems that the standard way to draw dots, lines, circles, and Bezier paths is to draw them in inside of drawRect. We don't directly call drawRect, but just let iOS call it and we can use [self setNeedsDisplay] to tell iOS to try to call drawRect when it can...
It also seems that we cannot rely on
[self setClearsContextBeforeDrawing: NO];
to not clear the background of the view before calling drawRect. Some details are in this question: UIView: how to do non-destructive drawing?
How about directly drawing on the screen -- without putting those code in drawRect. For example, in ViewController.m, have some code that directly draw dots, lines, circles on the screen. Is that possible?
Without having to drop into OpenGL, the closest you can do to get around the erasure is to convert the context as an image using something like CGBitmapContextCreateImage. From there, you can retain the image in memory (or write it to disk if necessary), and then when you redraw the view, you first draw this original image into the context and then overlay it with new content.

How to resize a custom UIView and maintain its custom draw in proportion?

I have a custom view with some drawing on it.
I want to resize it to a new proportion and I want the pattern I drew in it's drawRect to also be resized by the same proportion.
Is there anyway I can accomplish this without refreshing and redrawing everything.
This should be happening for you automatically with the default contentMode, which is UIViewContentModeScaleToFill. contentMode determines how to adjust the cached bitmap without forcing a new call to drawRect:. Also see contentStretch which allows you to control which part of the view is scaled.
you will have to redraw it for the new proportion.
For that you have to store the points that made the CGPath and scale the points according to the new proportion and render it again.
Redrawing CGPath needs attention.
If you have used simple moveTopoint / AddlinePoint you can do it just by storing points in an array. You can scale and redraw it later.
If you can used functions like addcurveTopoint etc., storing points in array won't work.A general purpose way is needed.For that you have to use the CGpathApply function. You can see an example it here. http://www.mlsite.net/blog/?p=1312
If you need to zoom and no interation neeeded you can take a scrrenshot and and zoom the image.

iOS : need inputs in developing efficient ( performance wise ) drawing app

I have this app using which one can draw basic shapes like rectangle, eclipse, circle, text etc.
I also allow free form drawing, which is stored as set-of-points, on the canvas.
Also a user can resize and move around these objects by operating on the selection handles that appear when an object is selected.
In addition the user should be able to zoom and pan the canvas.
I need some inputs on how to efficiently implement this drawing functionality.
I have following things in mind -
Use UIView's InvalidateRect and drawRect
Have a UIView for the main canvas and for each inserted object - invalidate the correspoding rect and redraw all the objects which intersects that rect in the drawRect function of the UIView.
Have a UIView and use CALayer ?
every one keep mentioning about the CALayer , I dont have much idea on this, before I venture into this I wanted a quick input on whether this route is worth taking.
like, https://developer.apple.com/library/ios/#qa/qa1708/_index.html
Have a UIImageView as canvas and when drawing each object, we do this
i) Draw the object into offscreen CGContext, basically, create a new CGContext by using UIGraphicsBeginImageContext, draw the shape, extract the image out of this CG context and use that as source of UIImageView's image property, but here how do I invalidate only a part of the UIImageView so that only that area gets refreshed.
Could you please suggest what is the best approach?
Is there any other efficient way to get this done?
Thanks.
Using a UIImage is more efficient for rendering multiple objects. But Using a CALayer is more efficient when moving and modifying a single object because you don't have to modify the other objects. So I think the best approach is to use a UIImage for general drawing and a CALayer for the shape that is being modified. In other words:
use a CALayer to draw the shape being added or modified, but don't draw it on the UIImage
use a UIImage to draw the other shapes
But OpenGL is still the most efficient solution, but don't bother with that if you don't have too many objects to draw.
If you want to draw polygons, you'll have to use Quartz framework, and have your drawing methods based on CALayer. It doesn't really matter which view you'll put your CALayers in, UIImageView or UIView. I'll say UIView since you won't be needing UIImageView's properties or methods for drawing.

Resources