Big Bucket Software you like to use
How to blend an 8-Bit Slide-to-Unlock widget
October 4th, 2010

We have a new screen in The Incident 1.2 that we felt warranted a new widget. It’s a sliding switch like the one you’ve no doubt seen many times on your iPhone’s lock screen:

Slide To Unlock

And here’s the one Neven mocked up in Photoshop:

Slide To Unlock

Looks awesome. Here’s what it took to bring it life.

The first step is the break the mockup into its component parts:

Slide To Unlock

The components would normally be neatly laid out in an atlas but they’re labelled here to make it easier for me to refer to them. A few things you’ll notice: The “Unlock” text doesn’t form part of the atlas. That’s because I render the text in code. This allows the widget to be easily localized and reused in instances where we don’t necessarily want to “Unlock” something. You may also be wondering what that yellow-white shine square is doing there. Notice the word “Unlock” and the way that part of the U, the n and l are lit. The yellow-white square is doing that. In the PSD, it’s layer sits above the text and is masked out by the shape of the text.

The next trick is to figure out how to blend it. OpenGL blending isn’t intuitive. At all. The most difficult part of making this work was figuring out how to mask out the shine. Here’s how I did it.

The first layer down is the frame, sort of. The frame is drawn to the screen but the “blending” is performed such that the frame appears black. It’ll make more sense in a minute. Here is how it looks:

Slide To Unlock

Ok so that isn’t too exciting. The interesting detail is the blend function, that is, what parameters were specified to glBlendFunc. Here’s the spec:

void glBlendFunc(GLenum sourceFactor, GLenum destinationFactor);

The blend function defines how the source colour and the destination colour combine to form the final colour. The source is what is being drawn and the destination is what is already on the surface being drawn onto, (in this case, the surface is the screen). Keep in mind that the alpha component forms part of the definition of “colour”.

The sliding switch’s frame is drawn with the following blend function:

glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);

That means, the pixels that make up the frame (source) are multiplied by 0 and the pixels already on the screen (destination) are multiplied by (1 – source alpha). In other words:

final pixel = 0 * source pixel + (1 – source pixel’s alpha) * destination pixel

Yes, it’s confusing. The upshot is, the pixels already on the screen are replaced with RGBA(0, 0, 0, 0) wherever the frame is opaque and the pixels already on the screen are unchanged wherever the frame is transparent, i.e. the corners.

Next, the “Unlock” text is rendered into the frame:

Slide To Unlock

The blend for the text is fairly standard:

glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

The upshot here is that the source pixels are always unaffected (multiplied by 1) and the destination pixels are unaffected wherever the source is transparent.

And here is where it starts to come together. Here is where I draw the shine onto the text:

Slide To Unlock

Ok, so how the heck did that happen. The blend function magic was:

glBlendFunc(GL_DST_ALPHA, GL_ONE);

So, the source pixels, (the shine), are multiplied by the destination alpha while the destination pixels are unaffected. In other words, the shine will only appear on top of non-transparent pixels. Recall that the frame was originally drawn such that it wasn’t just black, it was black with an alpha value of 0 (transparent). Or, super-black if you will.

Just to make things extra-difficult, this is where I blend in the text shadow. The required blend mode is the opposite of what was used to blend in the shine. That is, the shadow should be drawn only onto transparent pixels, rather than opaque pixels. Note that the shadow should actually be black, but I’ve made it gray here so that it can actually be seen:

Slide To Unlock

And the blend function:

glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE);

Note that the source pixel is multiplied by (1 – destination alpha) instead of just destination alpha as it was in the case of the shine. That’s what I mean when I say opposite.

We’re nearly there now. Next is the arrow:

Slide To Unlock

This is drawn “normally”; just like the text.

Finally, it’s time to bring it all home by drawing the frame once more, only this time, with a different blend function:

Slide To Unlock

This was drawn with the same blend function as the shadow:

glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE);

The source pixel was multipled by (1 – destination alpha) such that a source pixel only appeared wherever the destination pixel was transparent. Effectively, it fills in all of the remaining transparent pixels.

And there it is: a fully blended slide-to-unlock switch. Looks pretty. But until it actually “does” anything, it’s no different to the mockup. Here’s a video of the final result:

It behaves just like the real thing. Truth be told, we don’t actually have anything in The Incident 1.2 that needs unlocking. We’re using this for something slightly different. More on that soon.

  • Hm. I wonder what this could look like if it was made into a Winterboard theme for jailbroken devices. Imagine the whole of iOS in 8-bit style! 😀

  • Archagon — 10:17 pm on 4.10.2010

    Hey! I’m just starting to write an iPhone game, and I’m curious: why did you choose OpenGL over Core Animation? It doesn’t look like there’s anything in The Incident that requires OpenGL effects. Is it just because it’s easier to write something from scratch in OpenGL? Or maybe because you have so many items on-screen at once? Please elucidate me!

  • Lewis Herd — 12:55 am on 5.10.2010

    Hi Guys,
    When can we expect to see The Incident 1.2 Live?
    Also, can’t wait to see what this is for!

  • @Archagon OpenGL will generally scale better for games than Core Animation. CA is really good with animating parts of user interfaces, but GL excels at the kinds of effects that video games use. GL is more low level than CA, but with that you get the ability to draw more objects more efficiently.

    I would recommend learning GL for game programming — this doesn’t mean you always need to use GL for games, but it is a great tool to have at your disposal. I’ve used both CA and GL for games on the iPhone and they both have their place.

  • @Archagon What Dennis said. I tried to write my first game, Pocketball, using CoreAnimation/CoreImage/Quartz. That was 2 years ago. It may have grown (gotten faster) since then, but 2 years ago, it was impossibly slow. If you’re going to try CA, make sure you regularly test on a slower device (at least 3G).

    GL has a learning curve, but for anything graphics intensive (like games), it’s the way to go.

  • Archagon — 6:20 pm on 6.10.2010

    Thanks for the advice!

    It’s not the learning curve that bothers me. I just don’t want to use a lower level of abstraction than I need to, so that my code is more maintainable and works a little better with the iOS frameworks. With that said, I’ll definitely consider using OpenGL for my project.