The Transparent Border trick
May 18 2012

CoreAnimation is fast. Everything in a UIView is rendered to the screen via CALayers. Most of the time, you use it to composite rectangular images to the screen aligned to pixel boundaries and it’s fast and looks great.
But, if you put a transform on a layer with content all the way to the edge, all of your nice pixel-perfect interface design gets messed up. The transformed layer doesn’t look like the Photoshop / Illustrator / CoreGraphics version because of all of these jaggies.
While less noticeable on Retina displays, jaggies still look gross. Smooth the look of your interface during an animation or interaction that has a 2D rotation or a 3D transform with the Transparent Border Trick.
Hover to see with or without a transparent border hack:
By adding a transparent border around the content of your CALayers, you can use the built-in bilinear filtering of CoreAnimation to reduce the jaggies.

To understand how this works, let’s contrast how CoreGraphics renders with CoreAnimation. CoreGraphics, as a CPU-based API, has a lot of flexibility in how it renders. By default, it’s shooting for quality over speed so it picks a pretty anti-aliasing algorithm to make the edges of shapes smooth. This can be disabled with the CGContextSetShouldAntialias() function, but if you’re using CoreGraphics it’s usually for quality or complexity over speed anyway.
Wouldn’t it be better if we just antialiased all CoreAnimation content?
CoreAnimation uses the GPU, which brings with it a different set of constraints. Everything1 rendered with OpenGL is composed of triangles, rendered via an optimized pipeline. This pipeline does not come with antialising (supersampling) turned on by default. If you’re rendering directly in OpenGL ES on recent iOS devices, you can render to a larger buffer than the screen (4 rendered pixels per output pixel) and then shrink it down by “resolving” this larger buffer into a regular framebuffer. The problem is that rendering to a buffer 4x the screen size (super-sampled anti-aliasing) is rather slow and consumes a lot of power and memory so the battery life goes down. There are some shortcuts taken like only sampling textures / running shaders once per output pixel (multi-sampled anti-aliasing), but it’s always a trade-off. Thus, Apple has chosen not to use supersampling in CoreAnimation, choosing efficency over render quality.
So, uh, what’s bi-linear filtering ?
The trick here is that instead of directly accounting for the edge of the layer, we’re going to sample multiple pixels from the content on the inside of a larger layer. You have more than one pixel sampled when the point you’re sampling from doesn’t lie exactly on the pixel center of one of the pixels in the image.
So, when the layer is transformed, it will sample some from the transparent border and some from the inside content, depending on how far the current pixel is along the edge of the two, lending a smoother appearance to the edge. This breaks down if the on-screen points for the inside and outside of the layer contents are too far apart.
If you’re still confused, check out this github repo with an example.
By the way, there is an implementation of -[UIImage transparentBorderImage:] in a blog post on vocaro.com that might be helpful if you have an existing image of the content you want transformed.
Further notes
Since this trick exploits interpolation on the GPU during texture sampling, it can also be used when you’re drawing content with OpenGL ES directly. The application to quads is obvious, but there are ways to extend this to other shapes with carefully constructed vertex attributes or textures.
Happy hacking!
1 Ok, technically you can use lines or points to render, but this is quite rare and probably what you want unless you’re making a particle system.
Vector Math with C++ and GLKit
February 27 2012
If you do a lot of graphics development, you’re sure to use lots of vector math. A while ago I wrote some C++ extensions to Apple’s GLKit Math utilities, but haven’t had a chance to explain why they would be helpful, and how they can improve your graphics code.
So, say you have a simple numerical solver that keeps track of the position of a ball over time written in straight C or Objective-C. You might have some code that looks like this:
Scalar math
float ball_x, ball_y, ball_z;
...
ball_x = ball_x + change_x * dt;
ball_y = ball_y + change_y * dt;
ball_z = ball_z + change_z * dt;
Structs
That’s a tad awkward, so defining a struct to contain x,y,z floats together makes a lot of sense:
typedef struct {
float x,y,z;
} vector3;
vector3 ball;
...
ball.x = ball.x + change.x * dt;
ball.y = ball.y + change.y * dt;
ball.z = ball.z + change.z * dt;
Functions operating on structs
Well that’s kind of better, but I want to do the same operation on every coordinate, and I don’t want to keep peppering around these identical bits of code everywhere, so I’ll define some convenience functions:
typedef struct {
float x,y,z;
} vector3;
vector3 ball;
vector3 mulVectorScalar(vector3 a, float k) {
vector3 r;
r.x = a.x * k;
r.y = a.y * k;
r.z = a.z * k;
return r;
}
vector3 addVector(vector3 a, vector3 b) {
vector3 r;
r.x = a.x + b.x;
r.y = a.y + b.y;
r.z = a.z + b.z;
return r;
}
...
ball = addVector(ball, mulVectorScalar(b, dt))
We can even do some optimizations for the processor architecture that we’re running on. For instance, ARMv7 (available on recent iOS devices) has a set of extensions called “NEON” that allow operations on multiple data simultaniously at a hardware level.
For iOS 5’s new GLKit framework, Apple created a set of functions like our addVector that use NEON vector instructions to accelerate operations on vectors and matrices without having to pepper your own code with ifdefs.
From <GLKit/GLKVector2.h>:
static __inline__ GLKVector2 GLKVector2Add(GLKVector2 vectorLeft, GLKVector2 vectorRight)
{
#if defined(__ARM_NEON__)
float32x2_t v = vadd_f32(*(float32x2_t *)&vectorLeft,
*(float32x2_t *)&vectorRight);
return *(GLKVector2 *)&v;
#else
GLKVector2 v = { vectorLeft.v[0] + vectorRight.v[0],
vectorLeft.v[1] + vectorRight.v[1] };
return v;
#endif
}
The problem now is that these function names addVector and mulVectorScalar have taken the place of much more intuitive operators like + and *. Also, if we decide that the ball only needs to be a 2-element vector instead of a 3-element vector, we would need to switch all of the places where we update its position to new functions and new structs.
If we switch over to c++ land, we have a tool (operator overloading) that will let us have something both concise and efficient.
#import "GLKMath_cpp.h"
GLKVector2 ball;
...
ball = ball + change * dt;
This is implemented in GLKVector2_cpp.h as follows:
#import <GLKit/GLKVector2.h>
...
__inline__ GLKVector2 operator + (const GLKVector2& left, const GLKVector2& right) {
return GLKVector2Add(left, right);
}
__inline__ GLKVector2 operator * (const float& left, const GLKVector2& right) {
return GLKVector2MultiplyScalar(right, left);
}
...
So, if you don’t mind switching to c++, you can make your vector math a lot clearer to read (and hopefully reduce typos). If you notice anything amiss in with these headers or anything you think should be added, please create an issue or send a pull request on github
Adding Feeds with Jekyll
February 17 2012
I’m loving not worrying about my blog getting hacked, though I haven’t had a lot of time to turn these ideas for posts into actual published material.
But so far my site didn’t have any idea about feeds. Static html pages are generated, but there is no way to subscribe.
I was contacted by someone who wanted to subscribe via RSS, so I thought I’d figure out how to make feeds work nicely with a static Jekyll site. I added a directory /feeds to my Jekyll directory with a couple of pages in there to generate the XML files for Atom and RSS feeds.
I used a slightly modified version of these templates to generate the feeds.
You can see the small amount of work I did to support feeds on github.