using Microsoft.Xna.Framework.Graphics; using System; using System.IO; using System.Threading; using Color = Microsoft.Xna.Framework.Color; namespace Barotrauma { /// /// Based on http://jakepoz.com/jake_poznanski__background_load_xna.html /// public static class TextureLoader { public static Texture2D PlaceHolderTexture { get; private set; } static TextureLoader() { BlendColorBlendState = new BlendState { ColorDestinationBlend = Blend.Zero, ColorWriteChannels = ColorWriteChannels.Red | ColorWriteChannels.Green | ColorWriteChannels.Blue, AlphaDestinationBlend = Blend.Zero, AlphaSourceBlend = Blend.SourceAlpha, ColorSourceBlend = Blend.SourceAlpha }; BlendAlphaBlendState = new BlendState { ColorWriteChannels = ColorWriteChannels.Alpha, AlphaDestinationBlend = Blend.Zero, ColorDestinationBlend = Blend.Zero, AlphaSourceBlend = Blend.One, ColorSourceBlend = Blend.One }; } public static void Init(GraphicsDevice graphicsDevice, bool needsBmp = false) { _graphicsDevice = graphicsDevice; _needsBmp = needsBmp; _spriteBatch = new SpriteBatch(_graphicsDevice); PlaceHolderTexture = new Texture2D(graphicsDevice, 32, 32); Color[] data = new Color[32 * 32]; for (int i = 0; i < 32 * 32; i++) { data[i] = Color.Magenta; } PlaceHolderTexture.SetData(data); } public static Texture2D FromFile(string path, bool preMultiplyAlpha = true) { try { using (Stream fileStream = File.OpenRead(path)) { var texture = Texture2D.FromStream(_graphicsDevice, fileStream); if (preMultiplyAlpha) { PreMultiplyAlpha(ref texture); } return texture; } } catch (Exception e) { DebugConsole.ThrowError("Loading texture \"" + path + "\" failed!", e); return null; } } public static Texture2D FromStream(Stream fileStream, bool preMultiplyAlpha = true) { try { var texture = Texture2D.FromStream(_graphicsDevice, fileStream); PreMultiplyAlpha(ref texture); return texture; } catch (Exception e) { DebugConsole.ThrowError("Loading texture from stream failed!", e); return null; } } private static void PreMultiplyAlpha(ref Texture2D texture) { UInt32[] data = new UInt32[texture.Width * texture.Height]; texture.GetData(data); for (int i = 0; i < data.Length; i++) { uint a = (data[i] & 0xff000000) >> 24; if (a == 0) { data[i] = 0; continue; } else if (a == uint.MaxValue) { continue; } uint r = (data[i] & 0x00ff0000) >> 16; uint g = (data[i] & 0x0000ff00) >> 8; uint b = (data[i] & 0x000000ff); // Monogame 3.7 needs the line below. a *= a; a /= 255; b *= a; b /= 255; g *= a; g /= 255; r *= a; r /= 255; data[i] = (a << 24) | (r << 16) | (g << 8) | b; } //not sure why this is needed, but it seems to cut the memory usage of the game almost in half //GetData/SetData might be leaking memory? int width = texture.Width; int height = texture.Height; texture.Dispose(); texture = new Texture2D(_graphicsDevice, width, height); texture.SetData(data); } private static readonly BlendState BlendColorBlendState; private static readonly BlendState BlendAlphaBlendState; private static GraphicsDevice _graphicsDevice; private static SpriteBatch _spriteBatch; private static bool _needsBmp; } }