Fastest way to draw shadows and blend layers in iOS

Published by Petrakeas in Software

Screen-shot-2011-09-02-at-6

And by fastest I mean performance wise. Right now I’m working on an application that has to perform custom drawing (bezier curves) in real time and I am trying to achieve an oscilloscope look. That’s why at first I tried to do everything using Core Graphics functions (mainly CGContext functions) inside a custom drawRect function in my custom UIView. Core Graphics is very powerful and you can almost do what Photoshop does, in a programmatic way. The downside is that many functions are quite slow because they don’t use hardware acceleration.

For example, I first loaded the background of my oscilloscope inside a custom drawRect using Core Graphics and then painted curves on it using some interesting blending modes. I figured that it was quite faster to have the background on a separate UIView and just draw the curves on my custom UIView (which was on top of the other). This is due to the fact that layer compositing (CALayers and UIviews) uses hardware acceleration. On the other hand, when the background image was rendered using Core Graphics it caused heavy use of CGSBlendRGBA8888toRGBA8888 as Instruments showed.

Another way to blend layers and images is to use CGLayers but they are also slow due to the same reason. So, the fastest way to blend layers and images is to place them in separate CALayers or UIViews (you might prefer using CALayers to UIViews when user won’t be able to interact with the object). The disadvantage of this method  is that you can only blend them the normal way (you can’t use color dodge blend mode for example).

Secondly, I wanted to achieve a glow effect, so I decided to use shadow with a bright color. At first I used CGContextSetShadowWithColor on the layer of my curves. It proved to be very slow and the CPU usage increased proportionally to the amount of blur. This was caused by the fact that the shadow is implemented using a correlation function. As a result I decided to try the same with the shadow function of the CALayer of my UIView (self.layer.shadowRadius=8; … ). I was surprised to see that it almost had no impact on the performance even for bigger pixel radius! I am not sure but maybe this shadow function is hardware accelerated or has a more optimized implementation.

To sum up, iOS has a lot of graphics frameworks, some of which are just wrappers for the low level ones and others have completely different implementation. Unfortunately you have to learn the hard way in order to find the one who suits you right. For example when I used UIGraphicsGetCurrentContext inside the init function of my UIView  to initialize a CGLayer, the CGContext at that point was not intended for a display. The result was that this CGLayer was not on graphics memory and the rendering was very slow. So, I had to initialize it inside drawrect! This is just an example of how the performance of your application can vary from small changes…

UPDATE: for even faster shadows (for paths) create a shadowpath. Read more here.

UPDATE2: I added some functions to create bezier curves easily. Read the post here.

Comments (1)

Investir en Bourse
April 17th, 2014 at 6:08 am

Que penser de cet article qui ma veritablement subjugez … encore ?