// Copyright (c) Craftwork Games. All rights reserved.
// Licensed under the MIT license.
// See LICENSE file in the project root for full license information.
using System;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace MonoGame.Extended.Graphics;
///
/// Represents a drawable 2D texture region with additional properties for rendering, such as position, scale,
/// rotation, and color.
///
public class Sprite : IColorable
{
private Texture2DRegion _textureRegion;
///
/// Gets or sets a value indicating whether this sprite is visible.
///
public bool IsVisible { get; set; }
///
/// Gets or sets the color mask used when rendering this sprite.
///
///
/// The color mask is applied to the sprite's texture by multiplying each pixel's color value with the specified
/// color. For example, setting the color to `Color.Red` will make the sprite appear as a red tint over its
/// texture.
///
public Color Color { get; set; }
///
/// Gets or sets the alpha transparency value used when rendering this sprite.
///
///
/// The alpha value should be between 0 (fully transparent) and 1 (fully opaque).
///
public float Alpha { get; set; }
///
/// Gets or sets the layer depth used when rendering this sprite.
///
///
/// Sprites with higher depth values are rendered on top of those with lower depth values.
///
public float Depth { get; set; }
///
/// Gets or sets the sprite effects to apply when rendering this sprite.
///
///
/// This specifies the desired horizontal and/or vertical flip effect of the sprite.
///
public SpriteEffects Effect { get; set; }
///
/// Gets or Sets an object that contains user defined data about this sprite.
///
public object Tag { get; set; }
///
/// Gets the size of the sprite.
///
public Point Size => TextureRegion.OriginalSize;
///
/// Gets or sets the origin of this sprite.
///
///
/// The origin is relative to the bounds of the texture region and represents the point around which the sprite is
/// rotated and scaled.
///
public Vector2 Origin { get; set; }
///
/// Gets or sets the normalized origin of this sprite relative to its texture region.
///
///
/// The normalized origin represents the origin as a fraction of the texture region's UV coordinates,
/// where (0, 0) is the top-left corner and (1, 1) is the bottom-right corner.
///
public Vector2 OriginNormalized
{
get { return new Vector2(Origin.X / TextureRegion.OriginalSize.Width, Origin.Y / TextureRegion.OriginalSize.Height); }
set { Origin = new Vector2(value.X * TextureRegion.OriginalSize.Width, value.Y * TextureRegion.OriginalSize.Height); }
}
///
/// Gets or sets the source texture region of this sprite.
///
/// Thrown when setting to a null texture region.
///
/// Thrown if the source texture of the assigned has been disposed of when setting.
///
public Texture2DRegion TextureRegion
{
get => _textureRegion;
set
{
ArgumentNullException.ThrowIfNull(value);
if (value.Texture.IsDisposed)
{
throw new ObjectDisposedException(nameof(value), $"The source {nameof(Texture2D)} of the {nameof(TextureRegion)} was disposed prior to setting this property.");
}
_textureRegion = value;
if (value.OriginNormalized.HasValue)
{
OriginNormalized = value.OriginNormalized.Value;
}
}
}
///
/// Initializes a new instance of the class with the specified texture.
///
/// The source texture of the sprite.
///
/// Thrown if the parameter is .
///
///
/// Thrown if the parameter was disposed of prior.
///
public Sprite(Texture2D texture)
: this(new Texture2DRegion(texture))
{
}
///
/// Initializes a new instance of the class with the specified texture region.
/// The sprite represents a renderable 2D image defined by the given texture region.
///
/// The source texture region of the sprite.
///
/// Thrown if the parameter is .
///
///
/// Thrown if the source texture of the parameter has been disposed of.
///
public Sprite(Texture2DRegion textureRegion)
{
ArgumentNullException.ThrowIfNull(textureRegion);
if (textureRegion.Texture.IsDisposed)
{
throw new ObjectDisposedException(nameof(textureRegion), $"The source {nameof(Texture2D)} of the {nameof(textureRegion)} was disposed prior.");
}
_textureRegion = textureRegion;
Alpha = 1.0f;
Color = Color.White;
IsVisible = true;
Effect = SpriteEffects.None;
OriginNormalized = textureRegion.OriginNormalized ?? Vector2.Zero;
Depth = 0.0f;
}
///
/// Initializes a new instance of the class by copying property values from an existing sprite.
///
///
/// Creates a shallow copy where the new sprite shares the same reference.
/// The reference is copied but the object itself is not cloned.
///
/// The sprite to copy from.
/// Thrown if is .
///
/// Thrown if the source texture of the of the provided source sprite has been disposed of.
///
public Sprite(Sprite source)
{
ArgumentNullException.ThrowIfNull(source);
ObjectDisposedException.ThrowIf(source.TextureRegion.Texture.IsDisposed, source.TextureRegion.Texture);
_textureRegion = source._textureRegion;
Alpha = source.Alpha;
Color = source.Color;
IsVisible = source.IsVisible;
Effect = source.Effect;
Depth = source.Depth;
Origin = source.Origin;
Tag = source.Tag;
}
///
/// Gets the bounding rectangle of the sprite in world/screen coordinates.
///
/// The transformation of the sprite.
/// The bounding rectangle of the sprite in world/screen coordinates.
public RectangleF GetBoundingRectangle(Transform2 transform)
{
return GetBoundingRectangle(transform.Position, transform.Rotation, transform.Scale);
}
///
/// Gets the bounding rectangle of the sprite in world/screen coordinates.
///
/// The xy-coordinate position of the sprite in world/screen coordinates.
/// The rotation, in radians, of the sprite.
/// The scale of the sprite.
/// The bounding rectangle of the sprite in world/screen coordinates.
public RectangleF GetBoundingRectangle(Vector2 position, float rotation, Vector2 scale)
{
var corners = GetCorners(position, rotation, scale);
var min = new Vector2(corners.Min(i => i.X), corners.Min(i => i.Y));
var max = new Vector2(corners.Max(i => i.X), corners.Max(i => i.Y));
return new RectangleF(min.X, min.Y, max.X - min.X, max.Y - min.Y);
}
///
/// Gets the corner points of the sprite in world/screen coordinates.
///
/// The xy-coordinate position of the sprite in world/screen coordinates.
/// The rotation, in radians, of the sprite.
/// The scale of the sprite.
/// The corner points of the sprite in world/screen coordinates.
public Vector2[] GetCorners(Vector2 position, float rotation, Vector2 scale)
{
var min = -Origin;
var max = min + new Vector2(TextureRegion.OriginalSize.Width, TextureRegion.OriginalSize.Height);
var offset = position;
if (scale != Vector2.One)
{
min *= scale;
max = max * scale;
}
var corners = new Vector2[4];
corners[0] = min;
corners[1] = new Vector2(max.X, min.Y);
corners[2] = max;
corners[3] = new Vector2(min.X, max.Y);
if (rotation != 0)
{
var matrix = Matrix.CreateRotationZ(rotation);
for (var i = 0; i < 4; i++)
{
corners[i] = Vector2.Transform(corners[i], matrix);
}
}
for (var i = 0; i < 4; i++)
{
corners[i] += offset;
}
return corners;
}
///
/// Creates a shallow copy of this sprite with the same texture region and property values.
///
///
/// The returned sprite shares the same reference.
/// The reference is copied but the object itself is not cloned.
///
/// A new instance with copied property values.
///
/// Thrown if the source texture of the of this sprite has been disposed of.
///
public Sprite Clone()
{
return new Sprite(this);
}
}