using System; using System.IO; namespace Otter { /// /// Class representing a texture. Can perform pixel operations on the CPU, but those will be /// pretty slow and shouldn't be used that much. /// public class Texture : IDisposable { #region Public Fields /// /// The default setting to use for smoothing textures. /// Much easier to set this at the start of a program rather than /// adjust the settings for every single texture you use. /// public static bool DefaultSmooth = false; #endregion Public Fields #region Internal Fields internal bool needsUpdate = false; internal SFML.Graphics.Texture texture; #endregion Internal Fields #region Private Fields SFML.Graphics.Image image; #endregion Private Fields #region Public Constructors /// /// Load a texture from a file path. /// /// The file path to load from. /// Determines if the cache should be checked first. public Texture(string source, bool useCache = true) { if (useCache) { texture = Textures.Load(source); } else { texture = new SFML.Graphics.Texture(source); } Source = source; texture.Smooth = DefaultSmooth; } /// /// Create a texture from a stream of bytes. /// /// The stream to load from. /// Determines if the cache should be checked first. public Texture(Stream stream, bool useCache = true) { if (useCache) { texture = Textures.Load(stream); } else { texture = new SFML.Graphics.Texture(stream); } Source = "stream"; texture.Smooth = DefaultSmooth; } /// /// Creates a new texture from a copy of another texture. No cache option for this. /// /// The texture to copy from. public Texture(Texture copy) { texture = new SFML.Graphics.Texture(copy.SFMLTexture); Source = copy.Source; texture.Smooth = DefaultSmooth; } /// /// Create a texture from a byte array. /// /// The byte array to load from. /// Determines if the cache should be checked first. public Texture(byte[] bytes, bool useCache = true) { if (useCache) { using (MemoryStream ms = new MemoryStream(bytes)) { texture = Textures.Load(ms); } } else { using (MemoryStream ms = new MemoryStream(bytes)) { texture = new SFML.Graphics.Texture(ms); } } Source = "byte array"; texture.Smooth = DefaultSmooth; } /// /// Creates an empty texture of width and height. This does not use the cache. /// /// The width of the texture. /// The height of the texture. public Texture(int width, int height) { if (width < 0) throw new ArgumentException("Width must be greater than 0."); if (height < 0) throw new ArgumentException("Height must be greater than 0."); texture = new SFML.Graphics.Texture((uint)width, (uint)height); Source = width + " x " + height + " texture"; texture.Smooth = DefaultSmooth; } #endregion Public Constructors #region Internal Constructors /// /// Load a texture from an SFML texture. /// /// internal Texture(SFML.Graphics.Texture texture) { this.texture = texture; } #endregion Internal Constructors #region Public Properties /// /// The height of the texture. /// public int Height { get { return (int)texture.Size.Y; } } /// /// The array of pixels in the texture in bytes. /// public byte[] Pixels { get { CreateImage(); return image.Pixels; } set { image = new SFML.Graphics.Image((uint)Width, (uint)Height, value); Update(); } } /// /// The rectangle created by the Texture's width and height. /// public Rectangle Region { get { return new Rectangle(0, 0, Width, Height); } } /// /// Determines if the source texture is smoothed when transformed. /// public bool Smooth { get { return texture.Smooth; } set { texture.Smooth = value; } } /// /// The file path source if the texture was loaded from a file. /// public string Source { get; private set; } /// /// The width of the Texture. /// public int Width { get { return (int)texture.Size.X; } } #endregion Public Properties #region Internal Properties internal SFML.Graphics.Texture SFMLTexture { get { return texture; } } #endregion Internal Properties #region Public Methods /// /// Copy pixels from one texture to another using blitting. /// /// /// /// /// /// public void CopyPixels(Texture from, int fromX, int fromY, int toX, int toY) { CreateImage(); from.CreateImage(); image.Copy(from.image, (uint)toX, (uint)toY, new SFML.Graphics.IntRect(fromX, fromY, from.Width, from.Height)); texture = new SFML.Graphics.Texture(image); needsUpdate = true; } public void CopyPixels(Texture from, int fromX, int fromY, int fromWidth, int fromHeight, int toX, int toY) { CreateImage(); from.CreateImage(); image.Copy(from.image, (uint)toX, (uint)toY, new SFML.Graphics.IntRect(fromX, fromY, fromWidth, fromHeight)); texture = new SFML.Graphics.Texture(image); needsUpdate = true; } /// /// Loads the image internally in the texture for image manipulation. This is /// handled automatically, but it's exposed so that it can be manually controlled. /// /// If set to true a new image will always be created instead of only when there is no image. public void CreateImage(bool forceNewImage = false) { if (image == null || forceNewImage) { image = texture.CopyToImage(); } } /// /// Get the Color from a specific pixel on the texture. /// Warning: This is slow! /// /// The x coordinate of the pixel to get. /// The y coordinate of the pixel to get. /// The Color of the pixel. public Color GetPixel(int x, int y) { if (x < 0) throw new ArgumentException("X must be greater or equal to than 0."); if (y < 0) throw new ArgumentException("Y must be greater or equal to than 0."); CreateImage(); return new Color(image.GetPixel((uint)x, (uint)y)); } /// /// Save the texture to a file. The supported image formats are bmp, png, tga and jpg. /// /// The file path to save to. The type of image is deduced from the extension. public void SaveToFile(string path) { CreateImage(); image.SaveToFile(path); } /// /// Sets the color of a specific pixel on the texture. /// /// The x coordinate of the pixel. /// The y coordinate of the pixel. /// The Color to set the pixel to. public void SetPixel(int x, int y, Color color) { if (x < 0) throw new ArgumentException("X must be greater than 0."); if (y < 0) throw new ArgumentException("Y must be greater than 0."); if (x > Width) throw new ArgumentException("X must be within the texture width."); if (y > Height) throw new ArgumentException("Y must be within the texture width."); CreateImage(); image.SetPixel((uint)x, (uint)y, color.SFMLColor); //texture = new SFML.Graphics.Texture(image); texture.Update(image); needsUpdate = true; } /// /// Sets the color of a rectangle of pixels on the texture. /// /// The x coordinate of the rectangle. /// The y coordinate of the rectangle. /// The width of the rectangle. /// The height of the rectangle. /// The color of the rectangle. public void SetRect(int x, int y, int width, int height, Color color) { if (x < 0) throw new ArgumentException("X must be greater than 0."); if (y < 0) throw new ArgumentException("Y must be greater than 0."); if (width < 0) throw new ArgumentException("Width must be greater than 0."); if (height < 0) throw new ArgumentException("Height must be greater than 0."); for (var xx = x; xx < x + width; xx++) { for (var yy = y; yy < y + height; yy++) { SetPixel(xx, yy, color); } } } /// /// Updates the texture to reflect changes made from SetPixel. /// public void Update() { if (needsUpdate) { texture.Update(image); needsUpdate = false; } } /// /// Updates the texture with a byte array. /// Note: Updates immediately. Probably not the fastest. /// /// The byte array containing our pixels. public void SetBytes(byte[] bytes) { texture.Update(bytes); } /// /// Updates the texture with a byte array, at the given position and size. /// Note: Updates immediately. Probably not the fastest. /// /// The byte array containing our pixels. /// The width of the section we are updating. /// The height of the section we are updating. /// The X coordinate of the section we are updating. /// The Y coordinate of the section we are updating. public void SetBytes(byte[] bytes, int width, int height, int x = 0, int y = 0) { texture.Update(bytes, (uint)width, (uint)height, (uint)x, (uint)y); } /// /// Dispose the SFML texture to clear up memory probably. /// Warning: might not want to do this since other Textures might be using the same cached texture! /// public void Dispose() { SFMLTexture.Dispose(); } #endregion Public Methods } }