using System;
using System.IO;
using Microsoft.Xna.Framework.Graphics;
// Original code from http://xboxforums.create.msdn.com/forums/t/67895.aspx
// Gang of Penguins_01 and Michael Leone
// Conversion to extensionmethods by Jakob "xnafan" Krarup (www.xnafan.net)
///
/// Class with extensionmethods for GraphicsDevice.
///
public static class GraphicsDeviceExtensions
{
#region Constants
//the format of the screenshots' filename (with three digits to make many screenshots sort properly alphabetically)
private const string ScreenshotFormat = "ScreenShot_{0:000}.png";
#endregion
#region public Screenshot code
///
/// Prepares for a screenshot by using a RenderTarget2D to write content to in Reach mode.
/// This method should be called before your spriteBatch.Begin(),
/// and the SaveScreenshot() method should be called after spriteBatch.End()
///
/// The GraphicsDevice to use
public static void PrepareScreenShot(this GraphicsDevice device)
{
if (device.GraphicsProfile == GraphicsProfile.Reach)
{
//sets the rendertarget to a new RenderTarget2D for this Draw()
device.SetRenderTarget(new RenderTarget2D(device,
device.PresentationParameters.BackBufferWidth,
device.PresentationParameters.BackBufferHeight));
}
}
///
/// Create a screenshot of the current screen. You should call ScreenshotPrepare() before your spriteBatch.Begin(), and this method after spriteBatch.End();
///
/// The GraphicsDevice to use
/// The filename to save to. If fileName is null - a "ScreenShot_[number].png" format is used and the screenshot is saved to the game's running directory (where the EXE is)
public static void SaveScreenshot(this GraphicsDevice device, string fileName = null)
{
switch (device.GraphicsProfile)
{
case GraphicsProfile.Reach:
device.SaveScreenShotReach(fileName);
break;
case GraphicsProfile.HiDef:
device.SaveScreenshotHiDef(fileName);
break;
}
}
#endregion
#region Internal screenshot code
//saves a screenshot from the GPU to disk - HiDef only!
internal static void SaveScreenshotHiDef(this GraphicsDevice device, string fileName = null)
{
//for storing the imagedata
byte[] screenData = new byte[device.PresentationParameters.BackBufferWidth * device.PresentationParameters.BackBufferHeight * 4];
//get the image as byte data
device.GetBackBufferData(screenData);
//use "using" to ensure .Dispose() is called
using (Texture2D texture = new Texture2D(device, device.PresentationParameters.BackBufferWidth, device.PresentationParameters.BackBufferHeight, false, device.PresentationParameters.BackBufferFormat))
{
//insert the imagedata in the Texture2D
texture.SetData(screenData);
//save the texture
SaveTexture(texture, fileName);
}
}
// Creates a screenshot of the current screen in Reach mode
internal static void SaveScreenShotReach(this GraphicsDevice device, string fileName = null)
{
//give useful advice if game is in Reach mode, but trying to use the SaveScreenshotHiDef method
if (device.GetRenderTargets().Length == 0 || device.GetRenderTargets()[0].RenderTarget == null)
{
throw new Exception("It seems you didn't call the PrepareScreenShotReach() method before your spriteBatch.Begin().");
}
//get the current RenderTarget as a Texture2D
Texture2D texture = (Texture2D)device.GetRenderTargets()[0].RenderTarget;
//stop using the RenderTarget2D from now on
device.SetRenderTarget(null);
//save the rendertarget as a screenshot
SaveTexture(texture, fileName);
}
#endregion
#region Helpermethods
//saves the texture
private static void SaveTexture(Texture2D texture, string fileName)
{
//if we need to generate a filename - find an available one
if (fileName == null)
{
fileName = GetUnusedFileName();
}
//try saving the texture
try
{
//use "using" to ensure proper closing even if exceptions occur
using (Stream st = new FileStream(fileName, FileMode.Create))
{
texture.SaveAsPng(st, texture.Width, texture.Height);
}
}
catch (Exception ex)
{
throw new Exception(string.Format("Error trying to save texture to '{0}'. Error is '{1}'.",
fileName,
ex.ToString()));
}
}
//gives you an unused filename
private static string GetUnusedFileName()
{
int i = 0;
string fileName;
do
{
//increment filenumber
i++;
//generate filename for testing
fileName = string.Format(ScreenshotFormat, i);
} while (File.Exists(fileName)); //keep incrementing filenumber till we find an unused name
return fileName;
}
#endregion
}