using System;
using System.IO;
using JetBrains.Annotations;
using Robust.Client.Interfaces.Graphics;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using YamlDotNet.RepresentationModel;
namespace Robust.Client.Graphics
{
///
/// Contains a texture used for drawing things.
///
[PublicAPI]
public abstract class Texture : IDirectionalTextureProvider
{
///
/// The width of the texture, in pixels.
///
public int Width => Size.X;
///
/// The height of the texture, in pixels.
///
public int Height => Size.Y;
///
/// The size of the texture, in pixels.
///
public Vector2i Size { get; }
protected Texture(Vector2i size)
{
Size = size;
}
public static Texture Transparent =>
IoCManager.Resolve().GetStockTexture(ClydeStockTexture.Transparent);
public static Texture White =>
IoCManager.Resolve().GetStockTexture(ClydeStockTexture.White);
public static Texture Black =>
IoCManager.Resolve().GetStockTexture(ClydeStockTexture.Black);
///
/// Loads a new texture an existing image.
///
/// The image to load.
/// The "name" of this texture. This can be referred to later to aid debugging.
///
/// Parameters that influence the loading of textures.
/// Defaults to if null.
///
/// The type of pixels of the image. At the moment, images must be .
public static Texture LoadFromImage(Image image, string name = null,
TextureLoadParameters? loadParameters = null) where T : unmanaged, IPixel
{
var manager = IoCManager.Resolve();
return manager.LoadTextureFromImage(image, name, loadParameters);
}
///
/// Loads an image from a stream containing PNG data.
///
/// The stream to load the image from.
/// The "name" of this texture. This can be referred to later to aid debugging.
///
/// Parameters that influence the loading of textures.
/// Defaults to if null.
///
public static Texture LoadFromPNGStream(Stream stream, string name = null,
TextureLoadParameters? loadParameters = null)
{
var manager = IoCManager.Resolve();
return manager.LoadTextureFromPNGStream(stream, name, loadParameters);
}
Texture IDirectionalTextureProvider.Default => this;
Texture IDirectionalTextureProvider.TextureFor(Direction dir)
{
return this;
}
}
///
/// Flags for loading of textures.
///
[PublicAPI]
public struct TextureLoadParameters
{
///
/// The default sampling parameters for the texture.
///
public TextureSampleParameters SampleParameters { get; set; }
///
/// If true, the image data will be treated as sRGB.
///
public bool Srgb { get; set; }
public static TextureLoadParameters FromYaml(YamlMappingNode yaml)
{
var loadParams = TextureLoadParameters.Default;
if (yaml.TryGetNode("sample", out YamlMappingNode sampleNode))
{
loadParams.SampleParameters = TextureSampleParameters.FromYaml(sampleNode);
}
if (yaml.TryGetNode("srgb", out var srgb))
{
loadParams.Srgb = srgb.AsBool();
}
return loadParams;
}
public static readonly TextureLoadParameters Default = new TextureLoadParameters
{
SampleParameters = TextureSampleParameters.Default,
Srgb = true
};
}
///
/// Sample flags for textures.
/// These are separate from ,
/// because it is possible to create "proxies" to existing textures
/// with different sampling parameters than the base texture.
///
[PublicAPI]
public struct TextureSampleParameters
{
// NOTE: If somebody is gonna add support for 3D/1D textures, change this doc comment.
// See the note on this page for why: https://www.khronos.org/opengl/wiki/Sampler_Object#Filtering
///
/// If true, use bi-linear texture filtering if the texture cannot be rendered 1:1
///
public bool Filter { get; set; }
///
/// Controls how to wrap the texture if texture coordinates outside 0-1 are accessed.
///
public TextureWrapMode WrapMode { get; set; }
public static TextureSampleParameters FromYaml(YamlMappingNode node)
{
var wrap = TextureWrapMode.None;
var filter = false;
if (node.TryGetNode("filter", out var filterNode))
{
filter = filterNode.AsBool();
}
if (node.TryGetNode("wrap", out var wrapNode))
{
switch (wrapNode.AsString())
{
case "none":
wrap = TextureWrapMode.None;
break;
case "repeat":
wrap = TextureWrapMode.Repeat;
break;
case "mirrored_repeat":
wrap = TextureWrapMode.MirroredRepeat;
break;
default:
throw new ArgumentException("Not a valid wrap mode.");
}
}
return new TextureSampleParameters {Filter = filter, WrapMode = wrap};
}
public static readonly TextureSampleParameters Default = new TextureSampleParameters
{
Filter = false,
WrapMode = TextureWrapMode.None
};
}
///
/// Controls behavior when reading texture coordinates outside 0-1, which usually wraps the texture somehow.
///
[PublicAPI]
public enum TextureWrapMode
{
///
/// Do not wrap, instead clamp to edge.
///
None = 0,
///
/// Repeat the texture.
///
Repeat,
///
/// Repeat the texture mirrored.
///
MirroredRepeat,
}
}