How do I tile a specific region of a sprite?

| | August 10, 2015

I have a sprite that I use as a sprite sheet, where regions of the image represent various UI elements (window corner, border, background, etc.).

I want to use a certain region of that sprite as a texture that I can tile over a larger destination rectangle.

Setting the SamplerState to LinearWrap and then calling SpriteBatch.Draw with destination rect and source rect ends up stretching that region of the image, not tiling it.

All the examples I’ve seen that implement tiling use the entire sprite – I only want to use a region of it, and tile that region.

Is there any way to do this?

Edit for clarity:

Here’s an example spritesheet

enter image description here

where I want that colored region. I have a rectangle that represents a window border, that is larger than the region in the source image.

Here’s what I want to end up with

enter image description here

Here’s the code I’ve been using.

spriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.LinearWrap, null, null);
spriteBatch.Draw(sourceTexture, windowTopBorderRect, sourceTextureRegionRect, Color.White, 0, Vector2.Zero, SpriteEffects.None, 0);

And what I get is the image stretched across the window border rectangle, instead of tiling the source region within that rectangle.

enter image description here

2 Responses to “How do I tile a specific region of a sprite?”

  1. I don’t think using LinearWrap will work in this case, the reason being that LinearWrap takes place automatically when the texture coordinates fall beyond the edges of the texture, but you’re using a source rectangle that falls entirely within the texture, so it’s not even doing anything.

    I can think of three solutions (in the order I’d probably choose):

    1. Create a new Texture2D and copy only that portion of the texture into the new one, or do this offline in your image editor. Then draw it with a source rectangle that has the same size as the destination rectangle. Since this source rectangle will be larger than the texture, wrapping will take place. And since they both have the same size, there won’t be any stretching.

      To be more specific what I mean is something like:

      Rectangle windowTopBorderRect = /* wherever you want it to draw */
      Rectangle sourceTextureRegionRect = new Rectangle(0, 0, windowTopBorderRect.Width, windowTopBorderRect.Height);
      

      You could also use the position parameter instead of the destinationRectangle and let the sourceRectangle determine the size.

    2. Alternatively do the tiling manually with multiple SpriteBatch.Draw calls. Basically calculate how many times you need to draw the texture to fill that space, and draw them in the loop. In the case of an horizontal strip like your example, the last draw call will probably need a source rectangle, to crop only the remaining portion that fits that space.

    3. You could also probably draw using a special pixel shader effect that calculates a new texture coordinate that wraps around the original source rectangle without stretching, but this might be tricky.

  2. Pass the SpriteBatch a rectangle instead of null for the sourceRectangle parameter. It is defined as a nullable type in the method signature so it is optional and defaults to the whole texture.

    Edit: You will also need to change your destination rect with each draw call. Sorry I left that out.

Leave a Reply