Posted on by

gfx2-iconIn today’s Graphics 2.0 tutorial, we’ll explore how to use repeating fills on display objects. This allows you to fill a larger display object with a “tiled pattern” in a variety of repetition modes like repeat, mirror repeat, or clamp to edge. For each pattern, you can set the x and y offset of the fill, rotate the repeating fill, and even scale the fill, all independently of the object. In addition, you can apply these fill settings via explicit declaration or even a gradual transition to achieve a variety of animated effects.

Getting Started

Let’s begin by creating a basic 512×340 newRect() object, centered on the screen. We won’t bother filling this with any solid fill color, since we’ll be applying a repeating fill in the next step.

local x, y = display.contentCenterX, display.contentCenterY
local object = display.newRect( x, y, 512, 340 )

Now let’s set the default texture wrapping mode for both the x and y directions. This is achieved by using display.setDefault(), the same API you’ve used in the past for setting your app’s default fill color, text color, background color, etc. In Graphics 2.0, this has been extended to the texture wrapping mode as well.

display.setDefault( "textureWrapX", "repeat" )
display.setDefault( "textureWrapY", "mirroredRepeat" )

The available texture wrapping modes are as follows:

  • “clampToEdge” — this is the default mode; it clamps the texture to either the x or y direction. Clamped fills will not repeat in the clamped direction.
  • “repeat” — the fill will repeat in a tiled manner across the entire span of the filled object, as if identical tiles were simply laid out in the same orientation, side by side.
  • “mirroredRepeat” — the fill will repeat in a mirrored pattern, wherein each neighboring tile will mirror the one beside it.

logo-150Next, we’ll fill the display object with a 256×256 image.

object.fill = { type="image", filename="corona-logo.png" }

This is the exact same method as described in the Fills and Strokes tutorial. As you can see, the fill image is stretched to fill the entire width and height. Thus, with our minimal amount of code so far, we’re not seeing any behavioral changes with the new texture wrapping modes — but we’ll address that in a moment.

IMPORTANT NOTE: if you use one of the non-default repeat modes like “repeat” or “mirroredRepeat,” the fill image must have Power-of-2 dimensions, i.e. 32, 64, 128, 256, 512, etc.

rect logo-repeat-0

Scaling the Fill

Obviously, filling the entire shape with one stretched image doesn’t result in a repeating fill. To get the intended visual repetition in place, we need to use scaling methods on the fill image. This requires just two additional lines of code:
logo-repeat-1

object.fill.scaleX = 0.5
object.fill.scaleY = 0.5

However, the fill image is still stretched — the GPU simply takes the stretched fill texture and scales it to 50% on each axis. Thus, to make our fill pattern retain its correct 1:1 ratio across the repeated fill, we can use a simple algorithm to calculate the proper scaling ratios for the filled region, regardless of its width and height:

local scaleFactorX = 1 ; local scaleFactorY = 1
if ( object.width > object.height ) then
   scaleFactorY = object.width / object.height
else
   scaleFactorX = object.height / object.width
end

Now, let’s adjust the repeating fill by multiplying each scale setting by its appropriate factor:
logo-repeat-2

object.fill.scaleX = 0.5 * scaleFactorX
object.fill.scaleY = 0.5 * scaleFactorY

The result is a 1:1 ratio for the fill repetition — meaning, the fill itself is at its original size of 256×256 pixels, repeating within the bounds of the filled object and overflowing outside the edges as expected.

Offsetting the Fill

One common factor you’ll notice in the previous examples is that the fill repetition is always centered within the filled object. No matter the scale, a fill “tile” will reside in the center of the object and then repeat outward in all directions, assuming you haven’t clamped either of the axes.

While this default centering will probably work in most cases, what if you want to offset the fill? For example, set the fill pattern to begin in the upper-left and then repeat to the right and downward? This is possible by adjusting the x and y values of the fill, as follows:
logo-repeat-3

object.fill.x = 0.5
object.fill.y = 0

Instead of passing a specific pixel value to the x and y values, a value between -1 and 1 is required. This tells the GPU to offset the pattern by a full repetition in one of four directions. So, setting 0.5 as the x property will shift the pattern half of one repetition distance on the x axis.

One important distinction is that setting a positive x value will shift the pattern to the left, while a negative x value will shift the object to the right. Similarly, a positive y value will offset the pattern in an upward direction, and negative y downward.

Also, note that if your fill pattern tile is not a divisible size in relation to the larger object — for example, a 256×256 tile filling a 820×480 object — you’ll need to calculate the proper offset values between -1 and 1 to achieve the accurate “corner alignment.” In such instances, the offset values will not be a predictable 0.5 or -0.5.

Rotating the Fill

logo-repeat-4Lastly, you can rotate the fill independently of the object:

object.fill.rotation = 30

In this instance, rotation is performed in familiar degrees from 0 to 360. Rotation is applied around the center of the object and cannot be set to an anchor point (fills do not accept the anchorX or anchorY properties).

Transitioning the Fill

A final, powerful feature of repeating fills is the ability to transition them via basic Corona transitions. This allows you to shift, rotate, or scale the fill pattern within the filled object, and independently of any transition that is being performed upon the filled object.

transition.to( object.fill, { time=4000, x=0.5 } )
transition.to( object.fill, { time=4000, rotation=80 } )
transition.to( object.fill, { time=4000, scaleX=0.5, scaleY=0.4 } )

This capability allows you to perform a variety of interesting effects upon an object, for example, a repeating water pattern “flowing” slowly across the filled object in an endless cycle:

object.fill = { type="image", filename="water_tile_repeating.png" }
local function repeatTrans()
   transition.to( object.fill, { time=4000, x=object.fill.x+0.5, onComplete=repeatTrans })
end
repeatTrans()

In Summary

Repeating fills bring a variety of new visual and animated possibilities to Corona SDK. If you’re a Pro or Enterprise subscriber with access to Graphics 2.0 Daily Builds, please download a recent build and start experimenting with the possibilities!


Posted by . Thanks for reading...

8 Responses to “Tutorial: Repeating Fills in Graphics 2.0”

  1. Chris

    Now that’s something I’ve been waiting for!
    Finally we can save some memory using Patterns and stuff – especially for Business Apps and GUI stuff.

    This whole Graphic 2.0 is making a totally new product out of Corona.
    I guess I’ll have to take my time and test everything ;)

    Reply
  2. Tom

    Does this work with masks? If not, still a great feature, but to have repeating masks is a weird little feature I’ve been trying to find for an effect I want.

    Reply
    • Brent

      Hi Tom,
      This definitely works with containers (which are technically masks)… the video demo of the “flowing water” uses a container. So, this should work fine with masks too.

      Brent

      Reply
  3. Norvin Altamirano

    Hello, I’ve got some questions on this one, first of all, how can we gather the image from an atlased one? on the other hand everytime I’m creating a new filled quad should I call the display.setDefault etc, isn’t a way to specify that in the proper fill so we don’t have to call the display everytime? seems something that if it’s mandatory can be made as a param.

    Reply
    • Brent Sorrentino

      Hello @Piotr,
      Yes, you can fill using frames from an image sheet, as follows:

      local paint = {
      type = “image”,
      sheet = imageSheet,
      frame = 2
      }

      In this case, “imageSheet” is a reference to an image sheet object created by “graphics.newImageSheet()”.

      Reply
    • Brent Sorrentino

      Hi Johannes,
      Yes, this supports dynamic image selection, assuming you have it set up properly in your config.lua.

      Reply

Leave a Reply

  • (Will Not Be Published)