Here's my code. It's uses lists right now, so it's not the most efficient, but it's not too hard to convert to arrays.
/// <summary>
/// Draws a tiled image using a sprite batch, over an area, from an initial start coordinate.
/// </summary>
/// <param name="spritebatch">Spritebatch used to draw the image</param>
/// <param name="image">Image to tile</param>
/// <param name="area">Area to tile image</param>
/// <param name="start">Start coordinate inside the image</param>
public static void DrawTiledImage(SpriteBatch spritebatch, Texture2D image, Rectangle area, Vector2 start)
{
List<Rectangle>[] rect = ComputeTilingRectangles(image, area, start);
for (int i = 0; i < rect[0].Count; i++)
{
spritebatch.Draw(image, rect[1][i], rect[0][i], Color.White);
}
}
/// <summary>
/// Computes a list of source and target rectangles used for tiling an image over an area, from an initial start coordinate.
/// </summary>
/// <param name="image">Image to tile</param>
/// <param name="area">Area to tile image</param>
/// <param name="start">Start coordinate inside the image</param>
/// <returns></returns>
public static List<Rectangle>[] ComputeTilingRectangles(Texture2D image, Rectangle area, Vector2 start)
{
List<Rectangle>[] rect = new List<Rectangle>[] {new List<Rectangle>(), new List<Rectangle>()};
int xs0 = (int)start.X;
int ys0 = (int)start.Y;
int xt0 = area.Left;
int yt0 = area.Top;
int xsmax = image.Width;
int ysmax = image.Height;
int xtmax = area.Right;
int ytmax = area.Bottom;
// create tiled source and target rectangles
int ys = ys0;
int yt = yt0;
// vertical tiling
while (yt < ytmax)
{
int xs = xs0;
int xt = xt0;
int height = Math.Min(ysmax - ys, ytmax - yt);
// horizontal tiling
while (xt < xtmax)
{
int width = Math.Min(xsmax - xs, xtmax - xt);
// source rectangle
rect[0].Add(new Rectangle(xs, ys, width, height));
// target rectangle
rect[1].Add(new Rectangle(xt, yt, width, height));
xs = 0;
xt += width;
}
ys = 0;
yt += height;
}
return rect;
}
EDIT: Oh right, forgot to explain how to use it for your situation. So, use DrawTiledImage(SpriteBatch spritebatch, Texture2D image, Rectangle area, Vector2 start) where
area = new Rectangle(0, 0, SCREEN WIDTH, SCREEN HEIGHT)
start = new Vector2(XPOSITION % image.Width, YPOSITION % image.Height)
where XPOSITION and YPOSITION are the scrolled positions of the map, and SCREEN WIDTH and SCREEN HEIGHT are the width and height of the screen you are drawing on (the resolution of the game). Since it uses source rectangles, this method won't draw anything outside the renderable area (the red tiles), and thus won't waste drawing time.