Today’s guest tutorial comes to you courtesy of Barry Swan, the lead programmer at Playlevel. Barry has been working with Corona SDK for several years, often using it to prototype and experiment with new features. His Corona-built apps include several games and bespoke business software. His programming experience includes writing his own quad-based 3D engine in Director, years before it got a proper one. He provides useful tips and code in the Corona forums, on the #corona IRC channel at irc.freenode.net, and on Twitter under the alias Rakoonic.


gfx2-iconAnother new addition to Corona’s Graphics 2.0 engine is the snapshot. This brings “render to texture” capability to the SDK. Snapshots allow you to create a single dynamic image from other images and manipulate it using 2.5D distortion and filter effects.

In some respects, snapshots are similar to display groups and containers, but the unique differences make them shine. To help explain why, here are some basic behavioral differences between snapshots, display groups, and containers:

Snapshot

  • Works like a rectangle but lets you add children like a group.
  • Children are clipped to bounds of rectangle.
  • Has an anchor point, and user-defined width and height.
  • Does not count as an additional texture unit layer in regards to devices.
  • Only updates its contents when you command it to.
  • Does accept the application of filters.
  • Can be distorted in 2.5D.
  • Can be used to compose and manipulate images regardless of the device screen.

Display Group

  • Used to enable manipulation of several display objects as one.
  • Does not have an anchor point.
  • Updates its contents every Runtime frame.
  • Does not accept the application of filters.
  • Cannot be distorted in 2.5D.

Container

  • Works the same as a display group but visually clips anything within its bounds.
  • Has an anchor point, and user-defined width and height.
  • Counts as an additional texture unit layer in regards to devices.
  • Updates its contents every Runtime frame.
  • Does not accept the application of filters.
  • Can not be distorted in 2.5D.

Snapshot Use Cases

Snapshots have several unique uses, including the following:

Alternative Views

The classic use are for things like monitors or billboards in games, as well as for reflections such as mirrors or water. While these are more commonly found in 3D games, they also apply to 2D — or 2.5D, now that we have the ability!

snapshot-normal snapshot-adjusted

Drawing and Storing a Complex Scene

Snapshots also enable you to set up a complicated or slow-to-render view, render it once, and use that image elsewhere in your app without the overhead of redrawing it.

Better yet, if you create a snapshot and don’t need to re-render or update its contents later, you can remove the objects that were used to create the snapshot and free up the related texture memory.

Just don’t get too carried away and create massive snapshots. If you do so, you’ll use up all of your video memory. Snapshots should be less than or equal to the device’s maximum texture size to avoid problems.

Applying Effects on Groups and Containers

Snapshots allow you to apply effects to objects that don’t support the direct application of filters such as display groups and containers.

Effects that Bleed Beyond the Object Bounds

Snapshots are also useful for effects that bleed beyond the boundary of the original object. While most filters work within the shape boundary, some filters operate beyond the original object bounds. The “blur” filter, for example, can feather the edge, but you won’t be able to see that if you just apply the filter directly to the shape. To achieve a blur of the entire vector shape, including the blurred edge of the shape, you create a snapshot that’s large enough to accommodate the blurred edge, place the shape into that snapshot, and then apply the filter on the snapshot. In the example below, the corner brackets indicate the bounds of the original shape (left) and the snapshot (right).

blur-shape blur-snapshot

Layered Filters

Consider the image below: the front layer consists of multiple images (trees) and this layer overlaps a single background image (mountains). Let’s assume that you’d like to apply a blur filter only to the tree layer. Because the blur on the trees requires the alpha channel “bleed” described above, the tree objects should be placed in a snapshot. Then, the blur filter can be applied to the snapshot, leaving the background image sharp and focused.

tree-blur

Multiplayer Split-Screen Games on One Device

While some people might recommend containers, snapshots have a few benefits in this usage, despite the slight hit to video memory and speed.

  • Snapshots do not affect the nested mask/alpha limit. Containers work like a mask, so they count against the texture unit limit.
  • Snapshots truly “clip” their contents. Again, because containers behave like a mask, they do not clip what they draw, but merely make things outside the bounds invisible. If you draw a lot of objects that go partially outside the view area for a player, snapshots may actually perform better than containers.
  • Snapshots can have filters applied, so you can apply all sorts of cool effects to a player’s view regardless of what the others see.

Configuring and Using Snapshots

Creating the Snapshot

Snapshots are created by specifying a width and height:

local snapshot = display.newSnapshot( width, height )

Adding Objects to the Snapshot

Snapshots have group-like behaviors, but you don’t place objects directly into the snapshot. Instead, you should add them to its group property which is functionally the same as a normal display group.

For example:

local snapshot = display.newSnapshot( width, height )
local snapshotGroup = snapshot.group
local myImage = display.newImage( "logo.png" )
snapshotGroup:insert( myImage )

As you can see from above, when you want to add objects to the snapshot, :insert() them into the snapshot group, not the snapshot itself.

Adding the Snapshot to a Group

To add the snapshot to a display group, ensure that you add the snapshot object itself, not the snapshot group:

local overlayGroup = display.newGroup()
overlayGroup:insert( snapshot )

Updating the Snapshot

A very unique feature of snapshots is the ability to update the snapshot image only when you want to. This is accomplished by first calling:

snapshot:invalidate()

This tells Corona that during the next screen refresh, it should redraw the contents of the snapshot.

Note that :invalidate() does not cause an immediate update, so if you change a few objects, invalidate the snapshot, and then move a few other objects around, the snapshot will display all of the changes made during that frame, not just the changes you made before calling :invalidate(). Fortunately, this means that you can invalidate the snapshot multiple times in a frame without any performance penalty, since you are not requesting a redraw until the next frame.

First Update is Automatic

The first time you create a snapshot, it gets updated automatically with the objects you place inside it. This is a shortcut and it saves you the task of calling :invalidate() immediately. After all, it’s reasonable to assume that you’ll create a snapshot and place some items within it during the same frame. However, this step is optional and you can begin with an empty snapshot. 

Manipulating the Snapshot

The actual snapshot object can be treated like any normal image. You can move it, rotate it, set its alpha property, or distort it in 2.5D by altering its .path property. If placed within a container, it will be cropped accordingly. Applying a filter to a snapshot is simple too — just remember to apply it directly to the snapshot object, not its group.

Snapshots and Anchor Points 

Snapshots use the new anchor point methods. Since snapshots do not exist in Graphics 1.0, they do not have any reference point equivalent. Thus, manipulation happens via the anchor point, which is the center of the snapshot by default. This means that both the anchorX and anchorY properties of the snapshot start with values of 0.5, unless these values have been changed by display.setDefault().

Limitations of Snapshots

Before you start using snapshots, a few current limitations should be acknowledged. Note that the snapshots feature is still in beta and may receive additional capabilities and method changes before official release.

  • There is no way to save a snapshot’s content directly to disk. If you need to save something generated by a snapshot, you’ll need to display it on screen and capture it as normal.
  • Snapshots have an overhead. They require their own space in video memory on the device, and not all graphics drivers will be optimized for using them. That being said, I’ve found snapshots to be quick enough, so long as you don’t use more than 1 or 2 full-screen snapshots.
  • You cannot have multiple copies of the same snapshot. While you can load the same image multiple times, a snapshot only gives you one “handle” to it. So, imagine that you have a tile-based platformer game and you wish to have an animated TV in the background. You can create this using a snapshot, and it will animate just fine, but you won’t be able to place multiple TVs on the screen at once.
  • Snapshots have a maximum practical size. Don’t attempt to render your entire 10000-pixel square “Tiled” level to a snapshot!

In Summary

As you can see, snapshots bring a powerful new feature to Corona SDK. In addition to the basic use cases outlined in this tutorial, their potential extends into far more advanced techniques like double buffering, scene pixelation, and other special effects. I plan to discuss some of these advanced effects in a future tutorial, but in the meantime, all Pro and Enterprise subscribers can download a recent Graphics 2.0 Daily Build and begin experimenting with snapshots.

  1. Cool info! Anyway to add example images for all these graphic 2.0 tutorials. I think it will really help visual the concepts which I think I am pretty new to many of us! Just my suggestion :)

    Thank you for all of these tutorials!

    Mo

  2. In principle it’s nice to have those new features…what makes it a bit hard is that they’re completely separate ‘types’ and you have to build the concept around one of those ‘types’. Changing will not be straightforward (ex. anchor points). What you as a developer will probably do is wrap the functionality in a ‘class’ DisplayContainer or similar and make it easy to change from one ‘type’ to another if a new idea comes to your head and makes a change necessary. Maybe I’m wrong but I don’t see this as making things easier…it’s a kind of organic grown featuritis without hiding complexity from the developer. It makes it necessary to rewrite a lot of code which would not have been the case if the ‘types’ would have been related or designed as an extension to an existing displayGroup behaviour.

    However it’s cool to see the growing of Corona and I’m fine with wrapping/hiding your code to keep flexibility and maintainability.

    Cheers, Armin

  3. Brent Sorrentino says:

    Hi all,
    Thanks for the feedback! I’ve updated a few of these sections with example images of what the sub-sections are talking about. Hopefully this clarifies it better and you see what snapshots should/can be used for.

    Brent

  4. I assume the “Multiplayer Games on One Device” section refers to splitscreen games. (Where it can be a problem that the objects of one player’s part of the screen overlap into the other player’s part of the screen.) If yes, it should say so.

  5. Thanks to Brent for the screenshots – they definitely help. I am hoping we can get a few updates more to this article to explain it more clearly, so thanks for the feedback, it is very much appreciated as this is my first article, and I obviously still have plenty to learn!

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title="" rel=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>