using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace MLEM.Extensions { /// /// A set of extensions for dealing with and /// public static class GraphicsExtensions { private static int lastWidth; private static int lastHeight; /// /// Sets the graphics device manager to fullscreen, properly taking into account the preferred backbuffer width and height to avoid lower resolutions for higher resolution screens. /// /// The graphics device manager /// True if fullscreen should be enabled, false if disabled /// Thrown when changing out of fullscreen mode before changing into fullscreen mode using this method public static void SetFullscreen(this GraphicsDeviceManager manager, bool fullscreen) { manager.IsFullScreen = fullscreen; if (fullscreen) { var view = manager.GraphicsDevice.Viewport; GraphicsExtensions.lastWidth = view.Width; GraphicsExtensions.lastHeight = view.Height; var curr = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode; manager.PreferredBackBufferWidth = curr.Width; manager.PreferredBackBufferHeight = curr.Height; } else { if (GraphicsExtensions.lastWidth <= 0 || GraphicsExtensions.lastHeight <= 0) throw new InvalidOperationException("Can't call SetFullscreen to change out of fullscreen mode without going into fullscreen mode first"); manager.PreferredBackBufferWidth = GraphicsExtensions.lastWidth; manager.PreferredBackBufferHeight = GraphicsExtensions.lastHeight; } manager.ApplyChanges(); } /// /// Save version of that doesn't reset window size to defaults /// /// The graphics device manager public static void ApplyChangesSafely(this GraphicsDeviceManager manager) { // If we don't do this, then applying changes will cause the // graphics device manager to reset the window size to the // size set when starting the game :V var view = manager.GraphicsDevice.Viewport; manager.PreferredBackBufferWidth = view.Width; manager.PreferredBackBufferHeight = view.Height; manager.ApplyChanges(); } /// /// Resets preferred width and height back to the window's default bound values. /// /// The graphics device manager /// The window whose bounds to use public static void ResetWidthAndHeight(this GraphicsDeviceManager manager, GameWindow window) { var (width, height) = (window.ClientBounds.Width, window.ClientBounds.Height); manager.PreferredBackBufferWidth = Math.Max(height, width); manager.PreferredBackBufferHeight = Math.Min(height, width); manager.ApplyChanges(); } /// /// Starts a new using the specified render target. /// The returned context automatically disposes when used in a using statement, which causes any previously applied render targets to be reapplied automatically. /// /// The graphics device /// The render target to apply /// public static TargetContext WithRenderTarget(this GraphicsDevice device, RenderTarget2D target) { return new TargetContext(device, target); } /// /// Represents a context in which a is applied. /// This class should be used with . /// public readonly struct TargetContext : IDisposable { private readonly GraphicsDevice device; private readonly RenderTargetBinding[] lastTargets; /// /// Creates a new target context with the given settings. /// /// The graphics device to apply the target on /// The target to apply public TargetContext(GraphicsDevice device, RenderTarget2D target) { this.device = device; #if FNA // RenderTargetCount doesn't exist in FNA but we still want the optimization in MG this.lastTargets = device.GetRenderTargets(); #else this.lastTargets = device.RenderTargetCount <= 0 ? null : device.GetRenderTargets(); #endif device.SetRenderTarget(target); } /// /// Disposes this target context, which causes the graphics device's previous render targets to be re-applied. /// public void Dispose() { this.device.SetRenderTargets(this.lastTargets); } } } }