diff --git a/CHANGELOG.md b/CHANGELOG.md index 123b0f7c..90db3c5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Updated project types to .Net.Sdk [@nkast](https://github.com/nkast) [#880](https://github.com/craftworkgames/MonoGame.Extended/pull/880) - Use `System.IO.Compression.ZLibStream` [@nkast](https://github.com/nkast) [#822](https://github.com/craftworkgames/MonoGame.Extended/pull/882) - `BitmapFont` refactored. Now supports all three BMFont export types (XML, Text, and Binary). [@AristurtleDev](https://github.com/AristurtleDev) [#877](https://github.com/craftworkgames/MonoGame.Extended/pull/887) +- `TextureRegion2D` and `NinePatchRegion2D` moved to `MonoGame.Exteneded.Graphics` namespace. [@AristurtleDev](https://github.com/AristurtleDev) +- `TextureREgion2D` and `NinePatchRegion2D` renamed to `TextureRegion` and `NinePatchRegion`. [@AristurtleDev](https://github.com/AristurtleDev) ### Removed - All projects now output build artifacts to a common `.artifacts` directory at the root of the repository. [@AristurtleDev](https://github.com/AristurtleDev) [#865](https://github.com/craftworkgames/MonoGame.Extended/pull/865) diff --git a/source/MonoGame.Extended.Content.Pipeline/TextureAtlases/TexturePackerWriter.cs b/source/MonoGame.Extended.Content.Pipeline/TextureAtlases/TexturePackerWriter.cs index 3fd15ffd..3285829e 100644 --- a/source/MonoGame.Extended.Content.Pipeline/TextureAtlases/TexturePackerWriter.cs +++ b/source/MonoGame.Extended.Content.Pipeline/TextureAtlases/TexturePackerWriter.cs @@ -34,12 +34,12 @@ namespace MonoGame.Extended.Content.Pipeline.TextureAtlases public override string GetRuntimeType(TargetPlatform targetPlatform) { - return "MonoGame.Extended.TextureAtlases.TextureAtlas, MonoGame.Extended"; + return "MonoGame.Extended.Graphics.Texture2DAtlas, MonoGame.Extended"; } public override string GetRuntimeReader(TargetPlatform targetPlatform) { - return "MonoGame.Extended.TextureAtlases.TextureAtlasReader, MonoGame.Extended"; + return "MonoGame.Extended.Content.ContentReaders.Texture2DAtlasReader, MonoGame.Extended"; } } -} \ No newline at end of file +} diff --git a/source/MonoGame.Extended.Gui/Controls/ComboBox.cs b/source/MonoGame.Extended.Gui/Controls/ComboBox.cs index f8867d24..abc5f8ab 100644 --- a/source/MonoGame.Extended.Gui/Controls/ComboBox.cs +++ b/source/MonoGame.Extended.Gui/Controls/ComboBox.cs @@ -1,8 +1,8 @@ using System.Linq; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Input; +using MonoGame.Extended.Graphics; using MonoGame.Extended.Input.InputListeners; -using MonoGame.Extended.TextureAtlases; namespace MonoGame.Extended.Gui.Controls { @@ -13,7 +13,7 @@ namespace MonoGame.Extended.Gui.Controls } public bool IsOpen { get; set; } - public TextureRegion2D DropDownRegion { get; set; } + public Texture2DRegion DropDownRegion { get; set; } public Color DropDownColor { get; set; } = Color.White; public override bool OnKeyPressed(IGuiContext context, KeyboardEventArgs args) diff --git a/source/MonoGame.Extended.Gui/Controls/ProgressBar.cs b/source/MonoGame.Extended.Gui/Controls/ProgressBar.cs index 6983d097..6344b32e 100644 --- a/source/MonoGame.Extended.Gui/Controls/ProgressBar.cs +++ b/source/MonoGame.Extended.Gui/Controls/ProgressBar.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; using Microsoft.Xna.Framework; -using MonoGame.Extended.TextureAtlases; +using MonoGame.Extended.Graphics; namespace MonoGame.Extended.Gui.Controls { @@ -27,7 +27,7 @@ namespace MonoGame.Extended.Gui.Controls } } - public TextureRegion2D BarRegion { get; set; } + public Texture2DRegion BarRegion { get; set; } public Color BarColor { get; set; } = Color.White; public event EventHandler ProgressChanged; diff --git a/source/MonoGame.Extended.Gui/Cursor.cs b/source/MonoGame.Extended.Gui/Cursor.cs index 638baecd..5a184ce0 100644 --- a/source/MonoGame.Extended.Gui/Cursor.cs +++ b/source/MonoGame.Extended.Gui/Cursor.cs @@ -1,11 +1,11 @@ using Microsoft.Xna.Framework; -using MonoGame.Extended.TextureAtlases; +using MonoGame.Extended.Graphics; namespace MonoGame.Extended.Gui { public class Cursor { - public TextureRegion2D TextureRegion { get; set; } + public Texture2DRegion TextureRegion { get; set; } public Color Color { get; set; } = Color.White; } } \ No newline at end of file diff --git a/source/MonoGame.Extended.Gui/Element.cs b/source/MonoGame.Extended.Gui/Element.cs index 0c364a9a..6c649bcf 100644 --- a/source/MonoGame.Extended.Gui/Element.cs +++ b/source/MonoGame.Extended.Gui/Element.cs @@ -3,7 +3,7 @@ using System.ComponentModel; using System.Reflection; using System.Text.Json.Serialization; using Microsoft.Xna.Framework; -using MonoGame.Extended.TextureAtlases; +using MonoGame.Extended.Graphics; namespace MonoGame.Extended.Gui { @@ -30,8 +30,8 @@ namespace MonoGame.Extended.Gui public Color BorderColor { get; set; } = Color.White; public int BorderThickness { get; set; } = 0; - private TextureRegion2D _backgroundRegion; - public TextureRegion2D BackgroundRegion + private Texture2DRegion _backgroundRegion; + public Texture2DRegion BackgroundRegion { get => _backgroundRegion; set diff --git a/source/MonoGame.Extended.Gui/GuiSpriteBatchRenderer.cs b/source/MonoGame.Extended.Gui/GuiSpriteBatchRenderer.cs index 6d1e8cec..327245af 100644 --- a/source/MonoGame.Extended.Gui/GuiSpriteBatchRenderer.cs +++ b/source/MonoGame.Extended.Gui/GuiSpriteBatchRenderer.cs @@ -2,6 +2,7 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using MonoGame.Extended.BitmapFonts; +using MonoGame.Extended.Graphics; using MonoGame.Extended.TextureAtlases; namespace MonoGame.Extended.Gui @@ -9,8 +10,8 @@ namespace MonoGame.Extended.Gui public interface IGuiRenderer { void Begin(); - void DrawRegion(TextureRegion2D textureRegion, Rectangle rectangle, Color color, Rectangle? clippingRectangle = null); - void DrawRegion(TextureRegion2D textureRegion, Vector2 position, Color color, Rectangle? clippingRectangle = null); + void DrawRegion(Texture2DRegion textureRegion, Rectangle rectangle, Color color, Rectangle? clippingRectangle = null); + void DrawRegion(Texture2DRegion textureRegion, Vector2 position, Color color, Rectangle? clippingRectangle = null); void DrawText(BitmapFont font, string text, Vector2 position, Color color, Rectangle? clippingRectangle = null); void DrawRectangle(Rectangle rectangle, Color color, float thickness = 1f, Rectangle? clippingRectangle = null); void FillRectangle(Rectangle rectangle, Color color, Rectangle? clippingRectangle = null); @@ -45,13 +46,13 @@ namespace MonoGame.Extended.Gui _spriteBatch.End(); } - public void DrawRegion(TextureRegion2D textureRegion, Rectangle rectangle, Color color, Rectangle? clippingRectangle = null) + public void DrawRegion(Texture2DRegion textureRegion, Rectangle rectangle, Color color, Rectangle? clippingRectangle = null) { if (textureRegion != null) _spriteBatch.Draw(textureRegion, rectangle, color, clippingRectangle); } - public void DrawRegion(TextureRegion2D textureRegion, Vector2 position, Color color, Rectangle? clippingRectangle = null) + public void DrawRegion(Texture2DRegion textureRegion, Vector2 position, Color color, Rectangle? clippingRectangle = null) { if (textureRegion != null) _spriteBatch.Draw(textureRegion, position, color, clippingRectangle); diff --git a/source/MonoGame.Extended.Gui/Serialization/GuiNinePatchRegion2DJsonConverter.cs b/source/MonoGame.Extended.Gui/Serialization/GuiNinePatchRegion2DJsonConverter.cs index 9be58e55..b98d8ef0 100644 --- a/source/MonoGame.Extended.Gui/Serialization/GuiNinePatchRegion2DJsonConverter.cs +++ b/source/MonoGame.Extended.Gui/Serialization/GuiNinePatchRegion2DJsonConverter.cs @@ -2,7 +2,7 @@ using MonoGame.Extended.Serialization; namespace MonoGame.Extended.Gui.Serialization { - public class GuiNinePatchRegion2DJsonConverter : NinePatchRegion2DJsonConverter + public class GuiNinePatchRegion2DJsonConverter : NinePatchJsonConverter { private readonly IGuiTextureRegionService _textureRegionService; diff --git a/source/MonoGame.Extended.Gui/Serialization/GuiTextureAtlasJsonConverter.cs b/source/MonoGame.Extended.Gui/Serialization/GuiTextureAtlasJsonConverter.cs index a0617cf8..3d5598fb 100644 --- a/source/MonoGame.Extended.Gui/Serialization/GuiTextureAtlasJsonConverter.cs +++ b/source/MonoGame.Extended.Gui/Serialization/GuiTextureAtlasJsonConverter.cs @@ -1,12 +1,12 @@ using System; using System.Text.Json; using Microsoft.Xna.Framework.Content; +using MonoGame.Extended.Graphics; using MonoGame.Extended.Serialization; -using MonoGame.Extended.TextureAtlases; namespace MonoGame.Extended.Gui.Serialization { - public class GuiTextureAtlasJsonConverter : ContentManagerJsonConverter + public class GuiTextureAtlasJsonConverter : ContentManagerJsonConverter { private readonly IGuiTextureRegionService _textureRegionService; @@ -17,7 +17,7 @@ namespace MonoGame.Extended.Gui.Serialization } /// - public override TextureAtlas Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override Texture2DAtlas Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var textureAtlas = base.Read(ref reader, typeToConvert, options); if (textureAtlas is not null) diff --git a/source/MonoGame.Extended.Gui/Serialization/GuiTextureRegionService.cs b/source/MonoGame.Extended.Gui/Serialization/GuiTextureRegionService.cs index cf9ab9e8..97d8c0d7 100644 --- a/source/MonoGame.Extended.Gui/Serialization/GuiTextureRegionService.cs +++ b/source/MonoGame.Extended.Gui/Serialization/GuiTextureRegionService.cs @@ -1,16 +1,15 @@ using System.Collections.Generic; +using MonoGame.Extended.Graphics; using MonoGame.Extended.Serialization; -using MonoGame.Extended.TextureAtlases; namespace MonoGame.Extended.Gui.Serialization { public interface IGuiTextureRegionService : ITextureRegionService { - IList TextureAtlases { get; } - IList NinePatches { get; } + IList TextureAtlases { get; } } public class GuiTextureRegionService : TextureRegionService, IGuiTextureRegionService { } -} \ No newline at end of file +} diff --git a/source/MonoGame.Extended.Gui/Skin.cs b/source/MonoGame.Extended.Gui/Skin.cs index 77476be0..a9f85ffe 100644 --- a/source/MonoGame.Extended.Gui/Skin.cs +++ b/source/MonoGame.Extended.Gui/Skin.cs @@ -1,17 +1,17 @@ using System; using System.Collections.Generic; using System.IO; -using MonoGame.Extended.BitmapFonts; using System.Linq; using System.Reflection; +using System.Text.Json; +using System.Text.Json.Serialization; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Content; +using MonoGame.Extended.BitmapFonts; using MonoGame.Extended.Collections; +using MonoGame.Extended.Graphics; using MonoGame.Extended.Gui.Controls; using MonoGame.Extended.Gui.Serialization; -using MonoGame.Extended.TextureAtlases; -using System.Text.Json.Serialization; -using System.Text.Json; namespace MonoGame.Extended.Gui { @@ -19,9 +19,9 @@ namespace MonoGame.Extended.Gui { public Skin() { - TextureAtlases = new List(); + TextureAtlases = new List(); Fonts = new List(); - NinePatches = new List(); + NinePatches = new List(); Styles = new KeyedCollection(s => s.Name ?? s.TargetType.Name); } @@ -29,13 +29,13 @@ namespace MonoGame.Extended.Gui public string Name { get; set; } [JsonPropertyOrder(1)] - public IList TextureAtlases { get; set; } + public IList TextureAtlases { get; set; } [JsonPropertyOrder(2)] public IList Fonts { get; set; } - [JsonPropertyOrder(3)] - public IList NinePatches { get; set; } + [JsonPropertyOrder(3)] + public IList NinePatches { get; set; } [JsonPropertyOrder(4)] public BitmapFont DefaultFont => Fonts.FirstOrDefault(); @@ -88,7 +88,7 @@ namespace MonoGame.Extended.Gui public static Skin FromStream(ContentManager contentManager, Stream stream, params Type[] customControlTypes) { - var options = GuiJsonSerializerOptionsProvider.GetOptions(contentManager, customControlTypes); + var options = GuiJsonSerializerOptionsProvider.GetOptions(contentManager, customControlTypes); return JsonSerializer.Deserialize(stream, options); } diff --git a/source/MonoGame.Extended.Particles/ParticleEmitter.cs b/source/MonoGame.Extended.Particles/ParticleEmitter.cs index 65ac488e..211fad77 100644 --- a/source/MonoGame.Extended.Particles/ParticleEmitter.cs +++ b/source/MonoGame.Extended.Particles/ParticleEmitter.cs @@ -3,9 +3,9 @@ using System.Collections.Generic; using System.ComponentModel; using System.Text.Json.Serialization; using Microsoft.Xna.Framework; +using MonoGame.Extended.Graphics; using MonoGame.Extended.Particles.Modifiers; using MonoGame.Extended.Particles.Profiles; -using MonoGame.Extended.TextureAtlases; namespace MonoGame.Extended.Particles { @@ -15,7 +15,7 @@ namespace MonoGame.Extended.Particles private float _totalSeconds; [JsonConstructor] - public ParticleEmitter(string name, TextureRegion2D textureRegion, int capacity, TimeSpan lifeSpan, Profile profile) + public ParticleEmitter(string name, Texture2DRegion textureRegion, int capacity, TimeSpan lifeSpan, Profile profile) { if (profile == null) throw new ArgumentNullException(nameof(profile)); @@ -32,7 +32,7 @@ namespace MonoGame.Extended.Particles Parameters = new ParticleReleaseParameters(); } - public ParticleEmitter(TextureRegion2D textureRegion, int capacity, TimeSpan lifeSpan, Profile profile) + public ParticleEmitter(Texture2DRegion textureRegion, int capacity, TimeSpan lifeSpan, Profile profile) : this(null, textureRegion, capacity, lifeSpan, profile) { } @@ -67,7 +67,7 @@ namespace MonoGame.Extended.Particles public Profile Profile { get; set; } public float LayerDepth { get; set; } public ParticleReleaseParameters Parameters { get; set; } - public TextureRegion2D TextureRegion { get; set; } + public Texture2DRegion TextureRegion { get; set; } /// /// Gets a value that indicates whether this instance of the class has been diff --git a/source/MonoGame.Extended.Particles/ParticleExtensions.cs b/source/MonoGame.Extended.Particles/ParticleExtensions.cs index c86edaef..0074181c 100644 --- a/source/MonoGame.Extended.Particles/ParticleExtensions.cs +++ b/source/MonoGame.Extended.Particles/ParticleExtensions.cs @@ -1,6 +1,6 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; -using MonoGame.Extended.TextureAtlases; +using MonoGame.Extended.Graphics; namespace MonoGame.Extended.Particles { diff --git a/source/MonoGame.Extended.Tiled/TiledMapTileset.cs b/source/MonoGame.Extended.Tiled/TiledMapTileset.cs index ed14e158..914010f6 100644 --- a/source/MonoGame.Extended.Tiled/TiledMapTileset.cs +++ b/source/MonoGame.Extended.Tiled/TiledMapTileset.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; -using MonoGame.Extended.TextureAtlases; +using MonoGame.Extended.Graphics; namespace MonoGame.Extended.Tiled { @@ -16,7 +16,7 @@ namespace MonoGame.Extended.Tiled int TileWidth { get; } int TileHeight { get; } Texture2D Texture { get; } - TextureRegion2D GetRegion(int column, int row); + Texture2DRegion GetRegion(int column, int row); } public class TiledMapTileset : ITileset @@ -38,11 +38,11 @@ namespace MonoGame.Extended.Tiled public string Name => Texture.Name; public Texture2D Texture { get; } - public TextureRegion2D GetRegion(int column, int row) + public Texture2DRegion GetRegion(int column, int row) { var x = Margin + column * (TileWidth + Spacing); var y = Margin + row * (TileHeight + Spacing); - return new TextureRegion2D(Texture, x, y, TileWidth, TileHeight); + return new Texture2DRegion(Texture, x, y, TileWidth, TileHeight); } public string Type { get; } diff --git a/source/MonoGame.Extended/BitmapFonts/BitmapFont.cs b/source/MonoGame.Extended/BitmapFonts/BitmapFont.cs index d3e8f246..79272012 100644 --- a/source/MonoGame.Extended/BitmapFonts/BitmapFont.cs +++ b/source/MonoGame.Extended/BitmapFonts/BitmapFont.cs @@ -10,6 +10,7 @@ using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using MonoGame.Extended.BitmapFonts.BmfTypes; +using MonoGame.Extended.Graphics; using MonoGame.Extended.TextureAtlases; namespace MonoGame.Extended.BitmapFonts; @@ -390,7 +391,7 @@ public sealed class BitmapFont { BmfCharsBlock charBlock = bmfFile.Characters[i]; Texture2D texture = pages[bmfFile.Pages[charBlock.Page]]; - TextureRegion2D region = new TextureRegion2D(texture, charBlock.X, charBlock.Y, charBlock.Width, charBlock.Height); + Texture2DRegion region = new Texture2DRegion(texture, charBlock.X, charBlock.Y, charBlock.Width, charBlock.Height); BitmapFontCharacter character = new BitmapFontCharacter((int)charBlock.ID, region, charBlock.XOffset, charBlock.YOffset, charBlock.XAdvance); characters.Add(character.Character, character); } diff --git a/source/MonoGame.Extended/BitmapFonts/BitmapFontCharacter.cs b/source/MonoGame.Extended/BitmapFonts/BitmapFontCharacter.cs index 64d3cc06..01225297 100644 --- a/source/MonoGame.Extended/BitmapFonts/BitmapFontCharacter.cs +++ b/source/MonoGame.Extended/BitmapFonts/BitmapFontCharacter.cs @@ -3,7 +3,7 @@ // See LICENSE file in the project root for full license information. using System.Collections.Generic; -using MonoGame.Extended.TextureAtlases; +using MonoGame.Extended.Graphics; namespace MonoGame.Extended.BitmapFonts; @@ -20,7 +20,7 @@ public sealed class BitmapFontCharacter /// /// Gets the texture region that contains the character's image. /// - public TextureRegion2D TextureRegion { get; } + public Texture2DRegion TextureRegion { get; } /// /// Gets the horizontal offset for rendering the character. @@ -50,7 +50,7 @@ public sealed class BitmapFontCharacter /// The horizontal offset for rendering the character. /// The vertical offset for rendering the character. /// The horizontal advance value for rendering the next character. - public BitmapFontCharacter(int character, TextureRegion2D textureRegion, int xOffset, int yOffset, int xAdvance) + public BitmapFontCharacter(int character, Texture2DRegion textureRegion, int xOffset, int yOffset, int xAdvance) { Character = character; TextureRegion = textureRegion; diff --git a/source/MonoGame.Extended/BitmapFonts/BitmapFontExtensions.cs b/source/MonoGame.Extended/BitmapFonts/BitmapFontExtensions.cs index 7a6852e7..f04c7c11 100644 --- a/source/MonoGame.Extended/BitmapFonts/BitmapFontExtensions.cs +++ b/source/MonoGame.Extended/BitmapFonts/BitmapFontExtensions.cs @@ -2,7 +2,7 @@ using System; using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; -using MonoGame.Extended.TextureAtlases; +using MonoGame.Extended.Graphics; namespace MonoGame.Extended.BitmapFonts { diff --git a/source/MonoGame.Extended/BitmapFonts/BitmapFontReader.cs b/source/MonoGame.Extended/BitmapFonts/BitmapFontReader.cs index 643fc410..6cd4162e 100644 --- a/source/MonoGame.Extended/BitmapFonts/BitmapFontReader.cs +++ b/source/MonoGame.Extended/BitmapFonts/BitmapFontReader.cs @@ -7,6 +7,7 @@ using System.Linq; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; using MonoGame.Extended.Content; +using MonoGame.Extended.Graphics; using MonoGame.Extended.TextureAtlases; namespace MonoGame.Extended.BitmapFonts @@ -45,7 +46,7 @@ namespace MonoGame.Extended.BitmapFonts var xOffset = reader.ReadInt16(); var yOffset = reader.ReadInt16(); var xAdvance = reader.ReadInt16(); - var textureRegion = new TextureRegion2D(textures[textureIndex], x, y, width, height); + var textureRegion = new Texture2DRegion(textures[textureIndex], x, y, width, height); regions[r] = new BitmapFontCharacter((int)character, textureRegion, xOffset, yOffset, xAdvance); } diff --git a/source/MonoGame.Extended/Serialization/JsonContentTypeReader.cs b/source/MonoGame.Extended/Content/ContentReaders/JsonContentTypeReader.cs similarity index 87% rename from source/MonoGame.Extended/Serialization/JsonContentTypeReader.cs rename to source/MonoGame.Extended/Content/ContentReaders/JsonContentTypeReader.cs index 1d5609ef..1833967b 100644 --- a/source/MonoGame.Extended/Serialization/JsonContentTypeReader.cs +++ b/source/MonoGame.Extended/Content/ContentReaders/JsonContentTypeReader.cs @@ -3,7 +3,7 @@ using System.Text.Json; using Microsoft.Xna.Framework.Content; -namespace MonoGame.Extended.Serialization +namespace MonoGame.Extended.Content.ContentReaders { public class JsonContentTypeReader : ContentTypeReader { diff --git a/source/MonoGame.Extended/TextureAtlases/TextureAtlasJsonContentTypeReader.cs b/source/MonoGame.Extended/Content/ContentReaders/Texture2DAtlasJsonContentTypeReader.cs similarity index 70% rename from source/MonoGame.Extended/TextureAtlases/TextureAtlasJsonContentTypeReader.cs rename to source/MonoGame.Extended/Content/ContentReaders/Texture2DAtlasJsonContentTypeReader.cs index 20a7ae6a..50ea6d60 100644 --- a/source/MonoGame.Extended/TextureAtlases/TextureAtlasJsonContentTypeReader.cs +++ b/source/MonoGame.Extended/Content/ContentReaders/Texture2DAtlasJsonContentTypeReader.cs @@ -1,14 +1,12 @@ -using System.IO; using System.Text.Json; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; -using MonoGame.Extended.Content; -using MonoGame.Extended.Serialization; -using ContentReaderExtensions = MonoGame.Extended.Content.ContentReaderExtensions; +using MonoGame.Extended.Graphics; +using MonoGame.Extended.TextureAtlases; -namespace MonoGame.Extended.TextureAtlases +namespace MonoGame.Extended.Content.ContentReaders { - public class TextureAtlasJsonContentTypeReader : JsonContentTypeReader + public class Texture2DAtlasJsonContentTypeReader : JsonContentTypeReader { private static TexturePackerFile Load(ContentReader reader) { @@ -16,12 +14,12 @@ namespace MonoGame.Extended.TextureAtlases return JsonSerializer.Deserialize(json); } - protected override TextureAtlas Read(ContentReader reader, TextureAtlas existingInstance) + protected override Texture2DAtlas Read(ContentReader reader, Texture2DAtlas existingInstance) { var texturePackerFile = Load(reader); var assetName = reader.GetRelativeAssetName(texturePackerFile.Metadata.Image); var texture = reader.ContentManager.Load(assetName); - var atlas = new TextureAtlas(assetName, texture); + var atlas = new Texture2DAtlas(assetName, texture); var regionCount = texturePackerFile.Regions.Count; diff --git a/source/MonoGame.Extended/TextureAtlases/TextureAtlasReader.cs b/source/MonoGame.Extended/Content/ContentReaders/Texture2DAtlasReader.cs similarity index 66% rename from source/MonoGame.Extended/TextureAtlases/TextureAtlasReader.cs rename to source/MonoGame.Extended/Content/ContentReaders/Texture2DAtlasReader.cs index bfc79343..b7603d90 100644 --- a/source/MonoGame.Extended/TextureAtlases/TextureAtlasReader.cs +++ b/source/MonoGame.Extended/Content/ContentReaders/Texture2DAtlasReader.cs @@ -1,16 +1,16 @@ using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; -using MonoGame.Extended.Content; +using MonoGame.Extended.Graphics; -namespace MonoGame.Extended.TextureAtlases +namespace MonoGame.Extended.Content.ContentReaders { - public class TextureAtlasReader : ContentTypeReader + public class Texture2DAtlasReader : ContentTypeReader { - protected override TextureAtlas Read(ContentReader reader, TextureAtlas existingInstance) + protected override Texture2DAtlas Read(ContentReader reader, Texture2DAtlas existingInstance) { var assetName = reader.GetRelativeAssetName(reader.ReadString()); var texture = reader.ContentManager.Load(assetName); - var atlas = new TextureAtlas(assetName, texture); + var atlas = new Texture2DAtlas(assetName, texture); var regionCount = reader.ReadInt32(); @@ -27,4 +27,4 @@ namespace MonoGame.Extended.TextureAtlases return atlas; } } -} \ No newline at end of file +} diff --git a/source/MonoGame.Extended/Graphics/NinePatch.cs b/source/MonoGame.Extended/Graphics/NinePatch.cs new file mode 100644 index 00000000..25d1ce62 --- /dev/null +++ b/source/MonoGame.Extended/Graphics/NinePatch.cs @@ -0,0 +1,120 @@ +// 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 Microsoft.Xna.Framework; + +namespace MonoGame.Extended.Graphics; + +/// +/// Represents a nine-patch texture. +/// +/// +/// +/// A nine-patch texture is a specialized texture object used for rendering scalable graphical assets, +/// particularly user interface (UI) elements.It consists of a single texture region subdivided into nine +/// distinct subregions. When rendered, the four corner subregions remain unscaled, preserving their original +/// dimensions. The top and bottom edge subregions are stretched horizontally, while the left and right edge +/// subregions are stretched vertically. The central subregion is scaled along both axes to fill the desired +/// dimensions. +/// +/// +/// This approach is highly beneficial for UI components that require dynamic scaling, such as containers for +/// menus, dialog boxes, or other resizable elements. By leveraging the nine-patch texture, these graphical +/// assets can be seamlessly scaled to different sizes while maintaining their visual integrity and preventing +/// undesirable distortions or stretching artifacts. +/// +/// +public class NinePatch +{ + /// The index representing the top-left patch. + public const int TopLeft = 0; + + /// The index representing the top-middle patch. + public const int TopMiddle = 1; + + /// The index representing the top-right patch. + public const int TopRight = 2; + + /// The index representing the middle-left patch. + public const int MiddleLeft = 3; + + /// The index representing the middle patch. + public const int Middle = 4; + + /// The index representing the middle-right patch. + public const int MiddleRight = 5; + + /// The index representing the bottom-left patch. + public const int BottomLeft = 6; + + /// The index representing the bottom-middle patch. + public const int BottomMiddle = 7; + + /// The index representing the bottom-right patch. + public const int BottomRight = 8; + + private readonly Texture2DRegion[] _patches; + + /// + /// Gets the name assigned to this nine-patch. + /// + public string Name { get; } + + public Thickness Padding { get; } + + /// + /// Gets a read-only span of the texture regions that make up the nine-patch. + /// + /// + /// Elements are in order of top-left, top-middle, top-right, middle-left, middle, middle-right, bottom-left, + /// bottom-middle, and bottom-right. + /// + public ReadOnlySpan Patches => _patches; + + /// + /// Initializes a new instance of the class with the specified patches. + /// + /// + /// The array should contain the elements in the order of top-left, top-middle, + /// top-right, middle-left, middle, middle-right, bottom-left, bottom-middle, and bottom-right. + /// + /// An array of nine objects. + /// Thrown if is null. + /// + /// Thrown if does not contain exactly nine elements. + /// + public NinePatch(Texture2DRegion[] patches) : this(patches, null) { } + + /// + /// Initializes a new instance of the class with the specified patches and name. + /// + /// An array of nine objects. + /// + /// The name of the nine-patch. If null or empty, a default name will be generated based on the texture name of the + /// top-left patch. + /// + /// Thrown if is null. + /// + /// Thrown if does not contain exactly nine elements. + /// + public NinePatch(Texture2DRegion[] patches, string name) + { + ArgumentNullException.ThrowIfNull(patches); + if (patches.Length != 9) + { + throw new ArgumentException($"{nameof(patches)} must contain exactly 9 elements.", nameof(patches)); + } + + if (string.IsNullOrEmpty(name)) + { + name = $"{patches[0].Texture.Name}-nine-patch"; + } + + _patches = patches; + Rectangle mid = patches[NinePatch.Middle].Bounds; + Padding = new Thickness(mid.Left, mid.Top, mid.Right, mid.Bottom); + Name = name; + } +} diff --git a/source/MonoGame.Extended/Graphics/SpriteBatch.Extensions.cs b/source/MonoGame.Extended/Graphics/SpriteBatch.Extensions.cs new file mode 100644 index 00000000..487a2924 --- /dev/null +++ b/source/MonoGame.Extended/Graphics/SpriteBatch.Extensions.cs @@ -0,0 +1,184 @@ +// 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.Reflection; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace MonoGame.Extended.Graphics; + +public static class SpriteBatchExtensions +{ + #region ----------------------------NinePatch----------------------------- + private static readonly Rectangle[] _patchCache = new Rectangle[9]; + private static Rectangle _rect = default; + + public static void Draw(this SpriteBatch spriteBatch, NinePatch ninePatchRegion, Rectangle destinationRectangle, Color color, Rectangle? clippingRectangle = null) + { + CreateDestinationPatches(ninePatchRegion, destinationRectangle); + ReadOnlySpan sourcePatches = ninePatchRegion.Patches; + + for (int i = 0; i < sourcePatches.Length; i++) + { + Rectangle source = sourcePatches[i].Bounds; + Rectangle destination = _patchCache[i]; + + if (clippingRectangle.HasValue) + { + source = ClipSourceRectangle(source, destination, clippingRectangle.Value); + destination = ClipDestinationRectangle(destination, clippingRectangle.Value); + Draw(spriteBatch, sourcePatches[i].Texture, source, destination, color, clippingRectangle); + } + else + { + if (destination.Width > 0 && destination.Height > 0) + { + spriteBatch.Draw(sourcePatches[i].Texture, destination, source, color); + } + } + } + } + + #endregion -------------------------NinePatch----------------------------- + + #region ----------------------------Texture2D----------------------------- + + public static void Draw(this SpriteBatch spriteBatch, Texture2D texture, Rectangle sourceRectangle, Rectangle destinationRectangle, Color color, Rectangle? clippingRectangle) + { + if (!ClipRectangles(ref sourceRectangle, ref destinationRectangle, clippingRectangle)) + return; + + if (destinationRectangle.Width > 0 && destinationRectangle.Height > 0) + { + spriteBatch.Draw(texture, destinationRectangle, sourceRectangle, color); + } + } + + #endregion -------------------------Texture2D----------------------------- + + #region ----------------------------TextureRegion----------------------------- + + public static void Draw(this SpriteBatch spriteBatch, Texture2DRegion textureRegion, Vector2 position, Color color, Rectangle? clippingRectangle = null) + { + Draw(spriteBatch, textureRegion, position, color, 0, Vector2.Zero, Vector2.One, SpriteEffects.None, 0, clippingRectangle); + } + + public static void Draw(this SpriteBatch spriteBatch, Texture2DRegion textureRegion, Vector2 position, Color color, + float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth, Rectangle? clippingRectangle = null) + { + var sourceRectangle = textureRegion.Bounds; + + if (clippingRectangle.HasValue) + { + var x = (int)(position.X - origin.X); + var y = (int)(position.Y - origin.Y); + var width = (int)(textureRegion.Width * scale.X); + var height = (int)(textureRegion.Height * scale.Y); + var destinationRectangle = new Rectangle(x, y, width, height); + + if (!ClipRectangles(ref sourceRectangle, ref destinationRectangle, clippingRectangle)) + { + // Clipped rectangle is empty, nothing to draw + return; + } + + position.X = destinationRectangle.X + origin.X; + position.Y = destinationRectangle.Y + origin.Y; + } + + spriteBatch.Draw(textureRegion.Texture, position, sourceRectangle, color, rotation, origin, scale, effects, layerDepth); + } + + public static void Draw(this SpriteBatch spriteBatch, Texture2DRegion textureRegion, Rectangle destinationRectangle, Color color, Rectangle? clippingRectangle = null) + { + Draw(spriteBatch, textureRegion.Texture, textureRegion.Bounds, destinationRectangle, color, clippingRectangle); + } + + + + #endregion -------------------------TextureRegion----------------------------- + + #region ----------------------------Utilities----------------------------- + private static void CreateDestinationPatches(NinePatch ninePatch, Rectangle destinationRect) + { + destinationRect.Deconstruct(out int x, out int y, out int width, out int height); + ninePatch.Padding.Deconstruct(out int topPadding, out int rightPadding, out int bottomPadding, out int leftPadding); + + int midWidth = width - leftPadding - rightPadding; + int midHeight = height - topPadding - bottomPadding; + int top = y + topPadding; + int right = x + width - rightPadding; + int bottom = y + height - bottomPadding; + int left = x + leftPadding; + + _patchCache[NinePatch.TopLeft] = new Rectangle(x, y, leftPadding, topPadding); + _patchCache[NinePatch.TopMiddle] = new Rectangle(left, y, midWidth, topPadding); + _patchCache[NinePatch.TopRight] = new Rectangle(right, y, rightPadding, topPadding); + _patchCache[NinePatch.MiddleLeft] = new Rectangle(x, top, leftPadding, midHeight); + _patchCache[NinePatch.Middle] = new Rectangle(left, top, midWidth, midHeight); + _patchCache[NinePatch.MiddleRight] = new Rectangle(right, top, rightPadding, midHeight); + _patchCache[NinePatch.BottomLeft] = new Rectangle(x, bottom, leftPadding, bottomPadding); + _patchCache[NinePatch.BottomMiddle] = new Rectangle(left, bottom, midWidth, bottomPadding); + _patchCache[NinePatch.BottomRight] = new Rectangle(right, bottom, rightPadding, bottomPadding); + } + private static bool ClipRectangles(ref Rectangle sourceRectangle, ref Rectangle destinationRectangle, Rectangle? clippingRectangle) + { + if (!clippingRectangle.HasValue) + return true; + + var originalDestination = destinationRectangle; + destinationRectangle = destinationRectangle.Clip(clippingRectangle.Value); + + if (destinationRectangle == Rectangle.Empty) + return false; // Clipped rectangle is empty, nothing to draw + + var scaleX = (float)sourceRectangle.Width / originalDestination.Width; + var scaleY = (float)sourceRectangle.Height / originalDestination.Height; + + int leftDiff = destinationRectangle.Left - originalDestination.Left; + int topDiff = destinationRectangle.Top - originalDestination.Top; + + sourceRectangle.X += (int)(leftDiff * scaleX); + sourceRectangle.Y += (int)(topDiff * scaleY); + sourceRectangle.Width = (int)(destinationRectangle.Width * scaleX); + sourceRectangle.Height = (int)(destinationRectangle.Height * scaleY); + + return true; + } + + private static Rectangle ClipSourceRectangle(Rectangle sourceRectangle, Rectangle destinationRectangle, Rectangle clippingRectangle) + { + var left = (float)(clippingRectangle.Left - destinationRectangle.Left); + var right = (float)(destinationRectangle.Right - clippingRectangle.Right); + var top = (float)(clippingRectangle.Top - destinationRectangle.Top); + var bottom = (float)(destinationRectangle.Bottom - clippingRectangle.Bottom); + var x = left > 0 ? left : 0; + var y = top > 0 ? top : 0; + var w = (right > 0 ? right : 0) + x; + var h = (bottom > 0 ? bottom : 0) + y; + + var scaleX = (float)destinationRectangle.Width / sourceRectangle.Width; + var scaleY = (float)destinationRectangle.Height / sourceRectangle.Height; + x /= scaleX; + y /= scaleY; + w /= scaleX; + h /= scaleY; + + return new Rectangle((int)(sourceRectangle.X + x), (int)(sourceRectangle.Y + y), (int)(sourceRectangle.Width - w), (int)(sourceRectangle.Height - h)); + } + + private static Rectangle ClipDestinationRectangle(Rectangle destinationRectangle, Rectangle clippingRectangle) + { + var left = clippingRectangle.Left < destinationRectangle.Left ? destinationRectangle.Left : clippingRectangle.Left; + var top = clippingRectangle.Top < destinationRectangle.Top ? destinationRectangle.Top : clippingRectangle.Top; + var bottom = clippingRectangle.Bottom < destinationRectangle.Bottom ? clippingRectangle.Bottom : destinationRectangle.Bottom; + var right = clippingRectangle.Right < destinationRectangle.Right ? clippingRectangle.Right : destinationRectangle.Right; + return new Rectangle(left, top, right - left, bottom - top); + } + + #endregion -------------------------Utilities----------------------------- + +} diff --git a/source/MonoGame.Extended/Graphics/Texture2DAtlas.cs b/source/MonoGame.Extended/Graphics/Texture2DAtlas.cs new file mode 100644 index 00000000..6118977a --- /dev/null +++ b/source/MonoGame.Extended/Graphics/Texture2DAtlas.cs @@ -0,0 +1,420 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace MonoGame.Extended.Graphics; + +/// +/// Represents a 2D texture atlas that contains a collection of texture regions. +/// +/// +/// +/// A texture atlas, also known as a tile map, tile engine, or sprite sheet, is a large image that contains a +/// collection of sub-images, or "textures", each representing a texture map for a specific part of a 2D or 3D model. +/// +/// +/// These sub-textures can be rendered by adjusting the texture coordinates (UV map) to reference the appropriate +/// part of the atlas. This technique allows efficient rendering in applications where many small textures are +/// frequently used. +/// +/// +/// By storing textures in a single atlas, the graphics hardware treats them as a single unit, which can save memory +/// and improve performance by reducing the number of rendering state changes. Binding one large texture once is +/// typically faster than binding multiple smaller textures individually. +/// +/// +/// However, careful alignment is necessary to avoid texture bleeding when using mipmapping, and to prevent artifacts +/// between tiles when using texture compression. +/// +/// +public class Texture2DAtlas : IEnumerable +{ + private readonly List _regionsByIndex = new List(); + private readonly Dictionary _regionsByName = new Dictionary(); + + /// + /// Gets the name of the texture atlas. + /// + public string Name { get; } + + /// + /// Gets the underlying 2D texture. + /// + public Texture2D Texture { get; } + + /// + /// Gets the number of regions in the atlas. + /// + public int RegionCount => _regionsByIndex.Count; + + /// + /// Gets the at the specified index. + /// + /// The index of the texture region. + /// The texture region at the specified index. + /// + /// Thrown if the value of the parameter is less than zero or greater than or equal to + /// the total number of regions in this atlas. + /// + public Texture2DRegion this[int index] => GetRegion(index); + + /// + /// Gets the with the specified name. + /// + /// The name of the texture region. + /// The texture region with the specified name. + /// + /// Thrown if this atlas does not contain a region with a name that matches the parameter. + /// + public Texture2DRegion this[string name] => GetRegion(name); + + /// + /// Initializes a new instance of the class with the specified texture. + /// + /// The texture to create the atlas from. + /// Thrown if is null. + /// Thrown if is disposed. + public Texture2DAtlas(Texture2D texture) : this(null, texture) { } + + /// + /// Initializes a new instance of the class with the specified name and texture. + /// + /// The name of the texture atlas. + /// The texture to create the atlas from. + /// Thrown if is null. + /// Thrown if is disposed. + public Texture2DAtlas(string name, Texture2D texture) + { + ArgumentNullException.ThrowIfNull(texture); + if (texture.IsDisposed) + { + throw new ObjectDisposedException(nameof(texture), $"{nameof(texture)} was disposed prior"); + } + + if (string.IsNullOrEmpty(name)) + { + name = $"{texture.Name}Atlas"; + } + + Name = name; + Texture = texture; + } + + /// + /// Creates a new texture region and adds it to this atlas. + /// + /// The x-coordinate of the region. + /// The y-coordinate of the region. + /// The width, in pixels, of the region. + /// The height, in pixels, of the region. + /// The created texture region. + public Texture2DRegion CreateRegion(int x, int y, int width, int height) => CreateRegion(null, new Rectangle(x, y, width, height)); + + /// + /// Creates a new texture region with the specified name and adds it to this atlas. + /// + /// The name of the texture region. + /// The x-coordinate of the region. + /// The y-coordinate of the region. + /// The width, in pixels, of the region. + /// The height, in pixels, of the region. + /// The created texture region. + /// + /// Thrown if a region with the same name as the parameter already exists in this atlas. + /// + public Texture2DRegion CreateRegion(string name, int x, int y, int width, int height) => CreateRegion(name, new Rectangle(x, y, width, height)); + + /// + /// Creates a new texture region and adds it to this atlas. + /// + /// The location of the region. + /// The size, in pixels, of the region. + /// The created texture region. + public Texture2DRegion CreateRegion(Point location, Size size) => CreateRegion(null, new Rectangle(location, size)); + + /// + /// Creates a new texture region with the specified name and adds it to this atlas. + /// + /// The name of the texture region. + /// The location of the region. + /// The size, in pixels, of the region. + /// The created texture region. + /// + /// Thrown if a region with the same name as the parameter already exists in this atlas. + /// + public Texture2DRegion CreateRegion(string name, Point location, Size size) => CreateRegion(name, new Rectangle(location, size)); + + /// + /// Creates a new texture region and adds it to this atlas. + /// + /// The bounds of the region. + /// The created texture region. + public Texture2DRegion CreateRegion(Rectangle bounds) => CreateRegion(null, bounds); + + /// + /// Creates a new texture region with the specified name and adds it to this atlas. + /// + /// The name of the texture region. + /// The bounds of the region. + /// The created texture region. + /// + /// Thrown if a region with the same name as the parameter already exists in this atlas. + /// + public Texture2DRegion CreateRegion(string name, Rectangle bounds) + { + Texture2DRegion region = new Texture2DRegion(Texture, name, bounds); + AddRegion(region); + return region; + } + + /// + /// Determines whether the atlas contains a region with the specified name. + /// + /// The name of the region. + /// + /// if the atlas contains a region with the specified name; otherwise, + /// . + /// + public bool ContainsRegion(string name) => _regionsByName.ContainsKey(name); + + /// + /// Gets the index of the region with the specified name. + /// + /// The name of the region. + /// The index of the region if found; otherwise, -1. + public int GetIndexOfRegion(string name) + { + for (int i = 0; i < _regionsByIndex.Count; i++) + { + if (_regionsByIndex[i].Name == name) + { + return i; + } + } + + return -1; + } + + /// + /// Gets the region at the specified index. + /// + /// The index of the region. + /// The region at the specified index. + /// + /// Throw if the value of the is less than zero or is greater than or equal to the total + /// number of regions in this atlas. + /// + public Texture2DRegion GetRegion(int index) => _regionsByIndex[index]; + + /// + /// Gets the region with the specified name. + /// + /// The name of the region. + /// The region with the specified name. + /// + /// Thrown if this atlas does not contain a region with a name that matches the parameter. + /// + public Texture2DRegion GetRegion(string name) => _regionsByName[name]; + + /// + /// Tries to get the region at the specified index. + /// + /// The index of the region. + /// + /// When this method returns, contains the region at the specified index, if the index is found; otherwise, + /// . + /// + /// + /// if the region is found at the specified index; otherwise, . + /// + public bool TryGetRegion(int index, out Texture2DRegion region) + { + region = default; + + if (index < 0 || index >= _regionsByIndex.Count) + { + return false; + } + + region = _regionsByIndex[index]; + return true; + } + + /// + /// Tries to get the region with the specified name. + /// + /// The name of the region. + /// + /// When this method returns, contains the region with the specified name, if the name is found; otherwise, + /// . + /// + /// + /// if the region is found with the specified name; otherwise, . + /// + public bool TryGetRegion(string name, out Texture2DRegion region) => _regionsByName.TryGetValue(name, out region); + + /// + /// Gets the regions at the specified indexes. + /// + /// The indexes of the regions to get. + /// An array of the regions at the specified indexes. + /// + /// Thrown if the value of any index in the parameter is less than zero or is greater + /// than or equal to the total number of regions in this atlas. + /// + public Texture2DRegion[] GetRegions(params int[] indexes) + { + Texture2DRegion[] regions = new Texture2DRegion[indexes.Length]; + for (int i = 0; i < indexes.Length; i++) + { + regions[i] = GetRegion(indexes[i]); + } + + return regions; + } + + /// + /// Gets the regions with the specified names. + /// + /// The names of the regions to get. + /// An array of the regions with the specified names. + /// + /// Thrown if a region is not found in this atlas with a name that matches any of the names in the + /// parameter. + /// + public Texture2DRegion[] GetRegions(params string[] names) + { + Texture2DRegion[] regions = new Texture2DRegion[names.Length]; + + for (int i = 0; i < names.Length; i++) + { + regions[i] = GetRegion(names[i]); + } + + return regions; + } + + /// + /// Removes the region at the specified index. + /// + /// The index of the region to remove. + /// + /// if the region is successfully removed; otherwise, . + /// + /// + /// Throw if the value of the is less than zero or is greater than or equal to the total + /// number of regions in this atlas. + /// + public bool RemoveRegion(int index) + { + if (TryGetRegion(index, out Texture2DRegion region)) + { + return RemoveRegion(region); + } + + return false; + } + + /// + /// Removes the region with the specified name. + /// + /// The name of the region to remove. + /// + /// if the region is successfully removed; otherwise, . + /// + public bool RemoveRegion(string name) + { + if (TryGetRegion(name, out Texture2DRegion region)) + { + return RemoveRegion(region); + } + + return false; + } + + /// + /// Removes all regions from the atlas. + /// + public void ClearRegions() + { + _regionsByIndex.Clear(); + _regionsByName.Clear(); + } + + private void AddRegion(Texture2DRegion region) + { + if (_regionsByName.ContainsKey(region.Name)) + { + throw new InvalidOperationException($"This {nameof(Texture2DAtlas)} already contains a {nameof(Texture2DRegion)} with the name '{region.Name}'"); + } + + _regionsByIndex.Add(region); + _regionsByName.Add(region.Name, region); + } + + private bool RemoveRegion(Texture2DRegion region) => _regionsByIndex.Remove(region) && _regionsByName.Remove(region.Name); + + /// + /// Returns an enumerator that iterates through the collection of texture regions. + /// + /// An enumerator that can be used to iterate through the collection. + public IEnumerator GetEnumerator() => _regionsByIndex.GetEnumerator(); + + /// + /// Returns an enumerator that iterates through the collection of texture regions. + /// + /// An enumerator that can be used to iterate through the collection. + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + /// + /// Creates a new from the specified texture by dividing it into regions. + /// + /// The name of the texture atlas. + /// The source texture to create the atlas from. + /// The width, in pixels, of each region. + /// The height, in pixels, of each region. + /// + /// The maximum number of regions to create. Defaults to . + /// + /// + /// The margin, in pixels, to leave around the edges of the texture. Defaults to 0. + /// + /// The spacing, in pixels, between regions. Defaults to 0. + /// A containing the created regions. + /// Thrown if is null. + /// Thrown if is disposed. + public static Texture2DAtlas Create(string name, Texture2D texture, int regionWidth, int regionHeight, + int maxRegionCount = int.MaxValue, int margin = 0, int spacing = 0) + { + var textureAtlas = new Texture2DAtlas(name, texture); + var count = 0; + var width = texture.Width - margin; + var height = texture.Height - margin; + var xIncrement = regionWidth + spacing; + var yIncrement = regionHeight + spacing; + + var columns = (width - margin + spacing) / xIncrement; + var rows = (height - margin + spacing) / yIncrement; + var totalRegions = columns * rows; + + for (var i = 0; i < totalRegions; i++) + { + var x = margin + (i % columns) * xIncrement; + var y = margin + (i / columns) * yIncrement; + + if (x >= width || y >= height) + break; + + textureAtlas.CreateRegion(x, y, regionWidth, regionHeight); + count++; + + if (count >= maxRegionCount) + return textureAtlas; + } + + return textureAtlas; + } + +} diff --git a/source/MonoGame.Extended/Graphics/Texture2DRegion.cs b/source/MonoGame.Extended/Graphics/Texture2DRegion.cs new file mode 100644 index 00000000..8d3e4939 --- /dev/null +++ b/source/MonoGame.Extended/Graphics/Texture2DRegion.cs @@ -0,0 +1,224 @@ +// 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 Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace MonoGame.Extended.Graphics; + +/// +/// Represents a region of a texture. +/// +public class Texture2DRegion +{ + /// + /// Gets the name assigned to this texture region when it was created. + /// + public string Name { get; } + + /// + /// Gets the texture associated with this texture region. + /// + public Texture2D Texture { get; } + + /// + /// Gets the top-left x-coordinate of the texture region within the texture. + /// + public int X { get; } + + /// + /// Gets the top-left y-coordinate of the texture region within the texture. + /// + public int Y { get; } + + /// + /// Gets the width, in pixels, of the texture region. + /// + public int Width { get; } + + /// + /// Gets the height, in pixels, of the texture region. + /// + public int Height { get; } + + /// + /// Gets the size of the texture region. + /// + public Size Size { get; } + + /// + /// Gets or sets the user-defined data associated with this texture region. + /// + public object Tag { get; set; } + + /// + /// Gets the bounds of the texture region within the texture. + /// + public Rectangle Bounds { get; } + + /// + /// Gets the top UV coordinate of the texture region. + /// + public float TopUV { get; } + + /// + /// Gets the right UV coordinate of the texture region. + /// + public float RightUV { get; } + + /// + /// Gets the bottom UV coordinate of the texture region. + /// + public float BottomUV { get; } + + /// + /// Gets the left UV coordinate of the texture region. + /// + public float LeftUV { get; } + + /// + /// Initializes a new instance of the class representing the entire texture. + /// + /// The texture to create the region from. + /// + /// Thrown if is . + /// + /// + /// Thrown if has been disposed prior. + /// + public Texture2DRegion(Texture2D texture) + : this(texture, null, 0, 0, texture.Width, texture.Height) { } + + /// + /// Initializes a new instance of the class representing the entire texture with the + /// specified name. + /// + /// The texture to create the region from. + /// The name of the texture region. + /// + /// Thrown if is . + /// + /// + /// Thrown if has been disposed prior. + /// + public Texture2DRegion(Texture2D texture, string name) + : this(texture, name, texture.Bounds.X, texture.Bounds.Y, texture.Bounds.Width, texture.Bounds.Height) { } + + /// + /// Initializes a new instance of the class with the specified region of the texture. + /// + /// The texture to create the region from. + /// The region of the texture to use. + /// + /// Thrown if is . + /// + /// + /// Thrown if has been disposed prior. + /// + public Texture2DRegion(Texture2D texture, Rectangle region) + : this(texture, null, region.X, region.Y, region.Width, region.Height) { } + + /// + /// Initializes a new instance of the class with the specified region of the texture. + /// + /// The texture to create the region from. + /// The top-left x-coordinate of the region within the texture. + /// The top-left y-coordinate of the region within the texture. + /// The width, in pixels, of the region. + /// The height, in pixels, of the region. + /// + /// Thrown if is . + /// + /// + /// Thrown if has been disposed prior. + /// + public Texture2DRegion(Texture2D texture, int x, int y, int width, int height) + : this(texture, null, x, y, width, height) { } + + /// + /// Initializes a new instance of the class with the specified region of the texture and + /// name. + /// + /// The texture to create the region from. + /// The name of the texture region. + /// The region of the texture to use. + /// + /// Thrown if is . + /// + /// + /// Thrown if has been disposed prior. + /// + public Texture2DRegion(Texture2D texture, string name, Rectangle region) + : this(texture, name, region.X, region.Y, region.Width, region.Height) { } + + /// + /// Initializes a new instance of the class with the specified region of the texture and + /// name. + /// + /// The texture to create the region from. + /// The name of the texture region. + /// The top-left x-coordinate of the region within the texture. + /// The top-left y-coordinate of the region within the texture. + /// The width, in pixels, of the region. + /// The height, in pixels, of the region. + /// + /// Thrown if is . + /// + /// + /// Thrown if has been disposed prior. + /// + public Texture2DRegion(Texture2D texture, string name, int x, int y, int width, int height) + { + ArgumentNullException.ThrowIfNull(texture); + if (texture.IsDisposed) + { + throw new ObjectDisposedException(nameof(texture)); + } + + if (string.IsNullOrEmpty(name)) + { + name = $"{texture.Name}({x}, {y}, {width}, {height})"; + } + + Name = name; + Texture = texture; + X = x; + Y = y; + Width = width; + Height = height; + Bounds = new Rectangle(x, y, width, height); + Size = new Size(width, height); + TopUV = Bounds.Top / (float)texture.Height; + RightUV = Bounds.Right / (float)texture.Width; + BottomUV = Bounds.Bottom / (float)texture.Height; + LeftUV = Bounds.Left / (float)texture.Width; + } + + // Used for unit tests only + internal Texture2DRegion(string name, Rectangle bounds) : this(name, bounds.X, bounds.Y, bounds.Width, bounds.Height) { } + + // Used for unit tests only + internal Texture2DRegion(string name, int x, int y, int width, int height) + { + Name = name; + Texture = null; + X = x; + Y = y; + Width = width; + Height = height; + Bounds = new Rectangle(x, y, width, height); + Size = new Size(width, height); + TopUV = Bounds.Top / 1.0f; + RightUV = Bounds.Right / 1.0f; + BottomUV = Bounds.Bottom / 1.0f; + LeftUV = Bounds.Left / 1.0f; + } + + /// + public override string ToString() + { + return $"{Name ?? string.Empty} {Bounds}"; + } +} diff --git a/source/MonoGame.Extended/Graphics/TextureRegion.Extensions.cs b/source/MonoGame.Extended/Graphics/TextureRegion.Extensions.cs new file mode 100644 index 00000000..ca4d9aa9 --- /dev/null +++ b/source/MonoGame.Extended/Graphics/TextureRegion.Extensions.cs @@ -0,0 +1,159 @@ +// 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 Microsoft.Xna.Framework; + +namespace MonoGame.Extended.Graphics; + +/// +/// Provides extension methods for the class. +/// +public static class TextureRegionExtensions +{ + /// + /// Gets a subregion of the specified texture region using the provided rectangle. + /// + /// The texture region to get the subregion from. + /// The rectangle defining the subregion. + /// A new representing the specified subregion. + /// + /// Thrown if is . + /// + /// + /// Thrown if source texture of the has been disposed prior. + /// + public static Texture2DRegion GetSubregion(this Texture2DRegion textureRegion, Rectangle region) => + textureRegion.GetSubregion(null, region.X, region.Y, region.Width, region.Height); + + /// + /// Gets a subregion of the specified texture region using the provided rectangle and name. + /// + /// The texture region to get the subregion from. + /// The name of the new subregion. + /// The rectangle defining the subregion. + /// A new representing the specified subregion. + /// + /// Thrown if is . + /// + /// + /// Thrown if source texture of the has been disposed prior. + /// + public static Texture2DRegion GetSubregion(this Texture2DRegion textureRegion, string name, Rectangle region) => + textureRegion.GetSubregion(name, region.X, region.Y, region.Width, region.Height); + + /// + /// Gets a subregion of the specified texture region using the provided coordinates and dimensions. + /// + /// The texture region to get the subregion from. + /// The top-left x-coordinate of the subregion within the texture region. + /// The top-left y-coordinate of the subregion within the texture region. + /// The width, in pixels, of the subregion. + /// The height, in pixels, of the subregion. + /// A new representing the specified subregion. + /// + /// Thrown if is . + /// + /// + /// Thrown if source texture of the has been disposed prior. + /// + public static Texture2DRegion GetSubregion(this Texture2DRegion textureRegion, int x, int y, int width, int height) => + textureRegion.GetSubregion(null, x, y, width, height); + + /// + /// Gets a subregion of the specified texture region using the provided name, coordinates, and dimensions. + /// + /// The texture region to get the subregion from. + /// The name of the new subregion. + /// The top-left x-coordinate of the subregion within the texture region. + /// The top-left y-coordinate of the subregion within the texture region. + /// The width, in pixels, of the subregion. + /// The height, in pixels, of the subregion. + /// A new representing the specified subregion. + /// + /// Thrown if is . + /// + /// + /// Thrown if source texture of the has been disposed prior. + /// + public static Texture2DRegion GetSubregion(this Texture2DRegion textureRegion, string name, int x, int y, int width, int height) + { + ArgumentNullException.ThrowIfNull(textureRegion); + + if (string.IsNullOrEmpty(name)) + { + name = $"{textureRegion.Texture.Name}({x}, {y}, {width}, {height})"; + } + + Rectangle region = textureRegion.Bounds.GetRelativeRectangle(x, y, width, height); + return new Texture2DRegion(textureRegion.Texture, name, region); + } + + /// + /// Creates a nine-patch from the specified texture region with the specified padding. + /// + /// The texture region to create the nine-patch from. + /// The padding to apply to each side of the nine-patch. + /// A new representing the created nine-patch. + /// + /// Thrown if is . + /// + /// + /// Thrown if source texture of the has been disposed prior. + /// + public static NinePatch CreateNinePatch(this Texture2DRegion textureRegion, Thickness padding) => + textureRegion.CreateNinePatch(padding.Left, padding.Top, padding.Right, padding.Bottom); + + /// + /// Creates a nine-patch from the specified texture region with uniform padding. + /// + /// The texture region to create the nine-patch from. + /// The padding to apply uniformly to all sides of the nine-patch. + /// A new representing the created nine-patch. + /// + /// Thrown if is . + /// + /// + /// Thrown if source texture of the has been disposed prior. + /// + public static NinePatch CreateNinePatch(this Texture2DRegion textureRegion, int padding) => + textureRegion.CreateNinePatch(padding, padding, padding, padding); + + /// + /// Creates a nine-patch from the specified texture region with non-uniform padding. + /// + /// The texture region to create the nine-patch from. + /// The padding on the left side of the nine-patch. + /// The padding on the top side of the nine-patch. + /// The padding on the right side of the nine-patch. + /// The padding on the bottom side of the nine-patch. + /// A new representing the created nine-patch. + /// + /// Thrown if is . + /// + /// + /// Thrown if source texture of the has been disposed prior. + /// + public static NinePatch CreateNinePatch(this Texture2DRegion textureRegion, int leftPadding, int topPadding, int rightPadding, int bottomPadding) + { + Texture2DRegion[] patches = new Texture2DRegion[9]; + + int middleWidth = textureRegion.Width - leftPadding - rightPadding; + int middleHeight = textureRegion.Height - topPadding - bottomPadding; + + patches[NinePatch.TopLeft] = textureRegion.GetSubregion(0, 0, leftPadding, topPadding); + patches[NinePatch.TopMiddle] = textureRegion.GetSubregion(leftPadding, 0, middleWidth, topPadding); + patches[NinePatch.TopRight] = textureRegion.GetSubregion(middleWidth, 0, rightPadding, topPadding); + + patches[NinePatch.MiddleLeft] = textureRegion.GetSubregion(0, topPadding, leftPadding, middleHeight); + patches[NinePatch.Middle] = textureRegion.GetSubregion(leftPadding, topPadding, middleWidth, middleHeight); + patches[NinePatch.MiddleRight] = textureRegion.GetSubregion(middleWidth, topPadding, rightPadding, middleHeight); + + patches[NinePatch.BottomLeft] = textureRegion.GetSubregion(0, middleHeight, leftPadding, bottomPadding); + patches[NinePatch.BottomMiddle] = textureRegion.GetSubregion(leftPadding, middleHeight, middleWidth, bottomPadding); + patches[NinePatch.BottomRight] = textureRegion.GetSubregion(middleWidth, middleHeight, rightPadding, bottomPadding); + + return new NinePatch(patches); + } +} diff --git a/source/MonoGame.Extended/Math/RectangleExtensions.cs b/source/MonoGame.Extended/Math/RectangleExtensions.cs deleted file mode 100644 index a6b917a5..00000000 --- a/source/MonoGame.Extended/Math/RectangleExtensions.cs +++ /dev/null @@ -1,71 +0,0 @@ -using Microsoft.Xna.Framework; - -namespace MonoGame.Extended -{ - public static class RectangleExtensions - { - /// - /// Gets the corners of the rectangle in a clockwise direction starting at the top left. - /// - public static Point[] GetCorners(this Rectangle rectangle) - { - var corners = new Point[4]; - corners[0] = new Point(rectangle.Left, rectangle.Top); - corners[1] = new Point(rectangle.Right, rectangle.Top); - corners[2] = new Point(rectangle.Right, rectangle.Bottom); - corners[3] = new Point(rectangle.Left, rectangle.Bottom); - return corners; - } - - /// - /// Gets the corners of the rectangle in a clockwise direction starting at the top left. - /// - public static Vector2[] GetCorners(this RectangleF rectangle) - { - var corners = new Vector2[4]; - corners[0] = new Vector2(rectangle.Left, rectangle.Top); - corners[1] = new Vector2(rectangle.Right, rectangle.Top); - corners[2] = new Vector2(rectangle.Right, rectangle.Bottom); - corners[3] = new Vector2(rectangle.Left, rectangle.Bottom); - return corners; - } - - public static Rectangle ToRectangle(this RectangleF rectangle) - { - return new Rectangle((int) rectangle.X, (int) rectangle.Y, (int) rectangle.Width, (int) rectangle.Height); - } - - public static RectangleF ToRectangleF(this Rectangle rectangle) - { - return new RectangleF(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height); - } - - public static Rectangle Clip(this Rectangle rectangle, Rectangle clippingRectangle) - { - var clip = clippingRectangle; - rectangle.X = clip.X > rectangle.X ? clip.X : rectangle.X; - rectangle.Y = clip.Y > rectangle.Y ? clip.Y : rectangle.Y; - rectangle.Width = rectangle.Right > clip.Right ? clip.Right - rectangle.X : rectangle.Width; - rectangle.Height = rectangle.Bottom > clip.Bottom ? clip.Bottom - rectangle.Y : rectangle.Height; - - if (rectangle.Width <= 0 || rectangle.Height <= 0) - return Rectangle.Empty; - - return rectangle; - } - - public static RectangleF Clip(this RectangleF rectangle, RectangleF clippingRectangle) - { - var clip = clippingRectangle; - rectangle.X = clip.X > rectangle.X ? clip.X : rectangle.X; - rectangle.Y = clip.Y > rectangle.Y ? clip.Y : rectangle.Y; - rectangle.Width = rectangle.Right > clip.Right ? clip.Right - rectangle.X : rectangle.Width; - rectangle.Height = rectangle.Bottom > clip.Bottom ? clip.Bottom - rectangle.Y : rectangle.Height; - - if(rectangle.Width <= 0 || rectangle.Height <= 0) - return RectangleF.Empty; - - return rectangle; - } - } -} \ No newline at end of file diff --git a/source/MonoGame.Extended/Math/Thickness.cs b/source/MonoGame.Extended/Math/Thickness.cs index dfecbc9d..6edbd5bf 100644 --- a/source/MonoGame.Extended/Math/Thickness.cs +++ b/source/MonoGame.Extended/Math/Thickness.cs @@ -90,6 +90,9 @@ namespace MonoGame.Extended return FromValues(values); } + public void Deconstruct(out int top, out int right, out int bottom, out int left) => + (top, right, bottom, left) = (Top, Right, Bottom, Left); + public override string ToString() { if (Left == Right && Top == Bottom) @@ -98,4 +101,4 @@ namespace MonoGame.Extended return $"{Left}, {Right}, {Top}, {Bottom}"; } } -} \ No newline at end of file +} diff --git a/source/MonoGame.Extended/Rectangle.Extensions.cs b/source/MonoGame.Extended/Rectangle.Extensions.cs new file mode 100644 index 00000000..9d7826d1 --- /dev/null +++ b/source/MonoGame.Extended/Rectangle.Extensions.cs @@ -0,0 +1,78 @@ +using System; +using Microsoft.Xna.Framework; + +namespace MonoGame.Extended; + +/// +/// Provides extension methods for the structure. +/// +public static class RectangleExtensions +{ + /// + /// Gets the corners of the rectangle in a clockwise direction starting at the top left. + /// + /// The rectangle to get the corners of. + /// An array of elements representing the corners of the rectangle. + public static Point[] GetCorners(this Rectangle rectangle) + { + var corners = new Point[4]; + corners[0] = new Point(rectangle.Left, rectangle.Top); + corners[1] = new Point(rectangle.Right, rectangle.Top); + corners[2] = new Point(rectangle.Right, rectangle.Bottom); + corners[3] = new Point(rectangle.Left, rectangle.Bottom); + return corners; + } + + /// + /// Converts the specified to a . + /// + /// The rectangle to convert. + /// The converted . + public static RectangleF ToRectangleF(this Rectangle rectangle) + { + return new RectangleF(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height); + } + + /// + /// Clips the specified rectangle against the specified clipping rectangle. + /// + /// The rectangle to clip. + /// The rectangle to clip against. + /// The clipped rectangle, or if the rectangles do not intersect. + public static Rectangle Clip(this Rectangle rectangle, Rectangle clippingRectangle) + { + var clip = clippingRectangle; + rectangle.X = clip.X > rectangle.X ? clip.X : rectangle.X; + rectangle.Y = clip.Y > rectangle.Y ? clip.Y : rectangle.Y; + rectangle.Width = rectangle.Right > clip.Right ? clip.Right - rectangle.X : rectangle.Width; + rectangle.Height = rectangle.Bottom > clip.Bottom ? clip.Bottom - rectangle.Y : rectangle.Height; + + if (rectangle.Width <= 0 || rectangle.Height <= 0) + return Rectangle.Empty; + + return rectangle; + } + + /// + /// Gets a rectangle that is relative to the specified source rectangle, with the specified offsets and dimensions. + /// + /// The source rectangle. + /// The x-coordinate of the relative rectangle, relative to the source rectangle. + /// The y-coordinate of the relative rectangle, relative to the source rectangle. + /// The width, in pixels, of the relative rectangle. + /// The height, in pixels, of the relative rectangle. + /// The relative rectangle, clipped to the source rectangle. + public static Rectangle GetRelativeRectangle(this Rectangle source, int x, int y, int width, int height) + { + int absoluteX = source.X + x; + int absoluteY = source.Y + y; + + Rectangle relative; + relative.X = MathHelper.Clamp(absoluteX, source.Left, source.Right); + relative.Y = MathHelper.Clamp(absoluteY, source.Top, source.Bottom); + relative.Width = Math.Max(Math.Min(absoluteX + width, source.Right) - relative.X, 0); + relative.Height = Math.Max(Math.Min(absoluteY + height, source.Bottom) - relative.Y, 0); + + return relative; + } +} diff --git a/source/MonoGame.Extended/RectangleF.Extensions.cs b/source/MonoGame.Extended/RectangleF.Extensions.cs new file mode 100644 index 00000000..9c18bda7 --- /dev/null +++ b/source/MonoGame.Extended/RectangleF.Extensions.cs @@ -0,0 +1,78 @@ +using System; +using Microsoft.Xna.Framework; + +namespace MonoGame.Extended; + +/// +/// Provides extension methods for the structure. +/// +public static class RectangleFExtensions +{ + /// + /// Gets the corners of the rectangle in a clockwise direction starting at the top left. + /// + /// The rectangle to get the corners of. + /// An array of elements representing the corners of the rectangle. + public static Vector2[] GetCorners(this RectangleF rectangle) + { + var corners = new Vector2[4]; + corners[0] = new Vector2(rectangle.Left, rectangle.Top); + corners[1] = new Vector2(rectangle.Right, rectangle.Top); + corners[2] = new Vector2(rectangle.Right, rectangle.Bottom); + corners[3] = new Vector2(rectangle.Left, rectangle.Bottom); + return corners; + } + + /// + /// Converts the specified to a . + /// + /// The rectangle to convert. + /// The converted . + public static Rectangle ToRectangle(this RectangleF rectangle) + { + return new Rectangle((int)rectangle.X, (int)rectangle.Y, (int)rectangle.Width, (int)rectangle.Height); + } + + /// + /// Clips the specified rectangle against the specified clipping rectangle. + /// + /// The rectangle to clip. + /// The rectangle to clip against. + /// The clipped rectangle, or if the rectangles do not intersect. + public static RectangleF Clip(this RectangleF rectangle, RectangleF clippingRectangle) + { + var clip = clippingRectangle; + rectangle.X = clip.X > rectangle.X ? clip.X : rectangle.X; + rectangle.Y = clip.Y > rectangle.Y ? clip.Y : rectangle.Y; + rectangle.Width = rectangle.Right > clip.Right ? clip.Right - rectangle.X : rectangle.Width; + rectangle.Height = rectangle.Bottom > clip.Bottom ? clip.Bottom - rectangle.Y : rectangle.Height; + + if (rectangle.Width <= 0 || rectangle.Height <= 0) + return RectangleF.Empty; + + return rectangle; + } + + /// + /// Gets a rectangle that is relative to the specified source rectangle, with the specified offsets and dimensions. + /// + /// The source rectangle. + /// The x-coordinate of the relative rectangle, relative to the source rectangle. + /// The y-coordinate of the relative rectangle, relative to the source rectangle. + /// The width, in pixels, of the relative rectangle. + /// The height, in pixels, of the relative rectangle. + /// The relative rectangle, clipped to the source rectangle. + public static RectangleF GetRelativeRectangle(this RectangleF source, float x, float y, float width, float height) + { + float absoluteX = source.X + x; + float absoluteY = source.Y + y; + + RectangleF relative; + relative.X = MathHelper.Clamp(absoluteX, source.Left, source.Right); + relative.Y = MathHelper.Clamp(absoluteY, source.Top, source.Bottom); + relative.Width = Math.Max(Math.Min(absoluteX + width, source.Right) - relative.X, 0); + relative.Height = Math.Max(Math.Min(absoluteY + height, source.Bottom) - relative.Y, 0); + + return relative; + } +} diff --git a/source/MonoGame.Extended/Serialization/NinePatchRegion2DJsonConverter.cs b/source/MonoGame.Extended/Serialization/NinePatchJsonConverter.cs similarity index 75% rename from source/MonoGame.Extended/Serialization/NinePatchRegion2DJsonConverter.cs rename to source/MonoGame.Extended/Serialization/NinePatchJsonConverter.cs index 44bbbf79..39bf38f8 100644 --- a/source/MonoGame.Extended/Serialization/NinePatchRegion2DJsonConverter.cs +++ b/source/MonoGame.Extended/Serialization/NinePatchJsonConverter.cs @@ -1,34 +1,34 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -using MonoGame.Extended.TextureAtlases; +using MonoGame.Extended.Graphics; namespace MonoGame.Extended.Serialization; /// -/// Converts a value to or from JSON. +/// Converts a value to or from JSON. /// -public class NinePatchRegion2DJsonConverter : JsonConverter +public class NinePatchJsonConverter : JsonConverter { private readonly ITextureRegionService _textureRegionService; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The texture region service used to retrieve texture regions. - public NinePatchRegion2DJsonConverter(ITextureRegionService textureRegionService) + public NinePatchJsonConverter(ITextureRegionService textureRegionService) { _textureRegionService = textureRegionService; } /// - public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(NinePatchRegion2D); + public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(NinePatch); /// /// - /// Thrown if the JSON property does not contain a properly formatted value + /// Thrown if the JSON property does not contain a properly formatted value /// - public override NinePatchRegion2D Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override NinePatch Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType != JsonTokenType.StartObject) { @@ -68,15 +68,14 @@ public class NinePatchRegion2DJsonConverter : JsonConverter var thickness = Thickness.Parse(padding); var region = _textureRegionService.GetTextureRegion(regionName); - - return new NinePatchRegion2D(region, thickness.Left, thickness.Top, thickness.Right, thickness.Bottom); + return region.CreateNinePatch(thickness); } /// /// /// Throw if is . /// - public override void Write(Utf8JsonWriter writer, NinePatchRegion2D value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, NinePatch value, JsonSerializerOptions options) { ArgumentNullException.ThrowIfNull(writer); diff --git a/source/MonoGame.Extended/TextureAtlases/TextureAtlasJsonConverter.cs b/source/MonoGame.Extended/Serialization/TextureAtlasJsonConverter.cs similarity index 86% rename from source/MonoGame.Extended/TextureAtlases/TextureAtlasJsonConverter.cs rename to source/MonoGame.Extended/Serialization/TextureAtlasJsonConverter.cs index 2d887091..fdf04064 100644 --- a/source/MonoGame.Extended/TextureAtlases/TextureAtlasJsonConverter.cs +++ b/source/MonoGame.Extended/Serialization/TextureAtlasJsonConverter.cs @@ -5,11 +5,12 @@ using System.Text.Json.Serialization; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; using MonoGame.Extended.Content; -using MonoGame.Extended.Serialization; +using MonoGame.Extended.Graphics; +using MonoGame.Extended.TextureAtlases; -namespace MonoGame.Extended.TextureAtlases +namespace MonoGame.Extended.Serialization { - public class TextureAtlasJsonConverter : JsonConverter + public class TextureAtlasJsonConverter : JsonConverter { private readonly ContentManager _contentManager; private readonly string _path; @@ -21,11 +22,11 @@ namespace MonoGame.Extended.TextureAtlases } /// - public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(TextureAtlas); + public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(Texture2DAtlas); - public override TextureAtlas Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override Texture2DAtlas Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - if(reader.TokenType == JsonTokenType.String) + if (reader.TokenType == JsonTokenType.String) { // TODO: (Aristurtle 05/20/2024) What is this for? It's just an if block that throws an exception. Need // to investigate. @@ -59,12 +60,12 @@ namespace MonoGame.Extended.TextureAtlases else texture = _contentManager.Load(textureDirectory + "/" + textureName); } - return TextureAtlas.Create(resolvedAssetName, texture, metadata.RegionWidth, metadata.RegionHeight); + return Texture2DAtlas.Create(resolvedAssetName, texture, metadata.RegionWidth, metadata.RegionHeight); } } /// - public override void Write(Utf8JsonWriter writer, TextureAtlas value, JsonSerializerOptions options) { } + public override void Write(Utf8JsonWriter writer, Texture2DAtlas value, JsonSerializerOptions options) { } // ReSharper disable once ClassNeverInstantiated.Local diff --git a/source/MonoGame.Extended/Serialization/TextureRegion2DJsonConverter.cs b/source/MonoGame.Extended/Serialization/TextureRegion2DJsonConverter.cs index dbcb66cf..47785790 100644 --- a/source/MonoGame.Extended/Serialization/TextureRegion2DJsonConverter.cs +++ b/source/MonoGame.Extended/Serialization/TextureRegion2DJsonConverter.cs @@ -1,14 +1,14 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -using MonoGame.Extended.TextureAtlases; +using MonoGame.Extended.Graphics; namespace MonoGame.Extended.Serialization; /// -/// Converts a value to or from JSON. +/// Converts a value to or from JSON. /// -public class TextureRegion2DJsonConverter : JsonConverter +public class TextureRegion2DJsonConverter : JsonConverter { private readonly ITextureRegionService _textureRegionService; @@ -26,10 +26,10 @@ public class TextureRegion2DJsonConverter : JsonConverter } /// - public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(TextureRegion2D); + public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(Texture2DRegion); /// - public override TextureRegion2D Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override Texture2DRegion Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var regionName = reader.GetString(); return string.IsNullOrEmpty(regionName) ? null : _textureRegionService.GetTextureRegion(regionName); @@ -43,7 +43,7 @@ public class TextureRegion2DJsonConverter : JsonConverter /// /// Thrown if is . /// - public override void Write(Utf8JsonWriter writer, TextureRegion2D value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, Texture2DRegion value, JsonSerializerOptions options) { ArgumentNullException.ThrowIfNull(writer); ArgumentNullException.ThrowIfNull(value); diff --git a/source/MonoGame.Extended/Serialization/TextureRegionService.cs b/source/MonoGame.Extended/Serialization/TextureRegionService.cs index 3914730a..57299101 100644 --- a/source/MonoGame.Extended/Serialization/TextureRegionService.cs +++ b/source/MonoGame.Extended/Serialization/TextureRegionService.cs @@ -1,35 +1,29 @@ using System.Collections.Generic; using System.Linq; +using MonoGame.Extended.Graphics; using MonoGame.Extended.TextureAtlases; namespace MonoGame.Extended.Serialization { public interface ITextureRegionService { - TextureRegion2D GetTextureRegion(string name); + Texture2DRegion GetTextureRegion(string name); } public class TextureRegionService : ITextureRegionService { public TextureRegionService() { - TextureAtlases = new List(); - NinePatches = new List(); + TextureAtlases = new List(); } - public IList TextureAtlases { get; } - public IList NinePatches { get; } + public IList TextureAtlases { get; } - public TextureRegion2D GetTextureRegion(string name) + public Texture2DRegion GetTextureRegion(string name) { - var ninePatch = NinePatches.FirstOrDefault(p => p.Name == name); - - if (ninePatch != null) - return ninePatch; - return TextureAtlases .Select(textureAtlas => textureAtlas.GetRegion(name)) .FirstOrDefault(region => region != null); } } -} \ No newline at end of file +} diff --git a/source/MonoGame.Extended/Sprites/ISpriteBatchDrawable.cs b/source/MonoGame.Extended/Sprites/ISpriteBatchDrawable.cs index ab314172..eedcf6fd 100644 --- a/source/MonoGame.Extended/Sprites/ISpriteBatchDrawable.cs +++ b/source/MonoGame.Extended/Sprites/ISpriteBatchDrawable.cs @@ -1,13 +1,13 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; -using MonoGame.Extended.TextureAtlases; +using MonoGame.Extended.Graphics; namespace MonoGame.Extended.Sprites { public interface ISpriteBatchDrawable { bool IsVisible { get; } - TextureRegion2D TextureRegion { get; } + Texture2DRegion TextureRegion { get; } Vector2 Position { get; } float Rotation { get; } Vector2 Scale { get; } diff --git a/source/MonoGame.Extended/Sprites/Sprite.cs b/source/MonoGame.Extended/Sprites/Sprite.cs index 2174c8a8..84247a77 100644 --- a/source/MonoGame.Extended/Sprites/Sprite.cs +++ b/source/MonoGame.Extended/Sprites/Sprite.cs @@ -2,15 +2,15 @@ using System.Linq; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; -using MonoGame.Extended.TextureAtlases; +using MonoGame.Extended.Graphics; namespace MonoGame.Extended.Sprites { public class Sprite : IColorable { - private TextureRegion2D _textureRegion; + private Texture2DRegion _textureRegion; - public Sprite(TextureRegion2D textureRegion) + public Sprite(Texture2DRegion textureRegion) { if (textureRegion == null) throw new ArgumentNullException(nameof(textureRegion)); @@ -25,7 +25,7 @@ namespace MonoGame.Extended.Sprites } public Sprite(Texture2D texture) - : this(new TextureRegion2D(texture)) + : this(new Texture2DRegion(texture)) { } @@ -58,7 +58,7 @@ namespace MonoGame.Extended.Sprites public Vector2 Origin { get; set; } public SpriteEffects Effect { get; set; } - public TextureRegion2D TextureRegion + public Texture2DRegion TextureRegion { get => _textureRegion; set diff --git a/source/MonoGame.Extended/Sprites/SpriteSheet.cs b/source/MonoGame.Extended/Sprites/SpriteSheet.cs index 6a8acb15..b3f22e95 100644 --- a/source/MonoGame.Extended/Sprites/SpriteSheet.cs +++ b/source/MonoGame.Extended/Sprites/SpriteSheet.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using MonoGame.Extended.Graphics; using MonoGame.Extended.TextureAtlases; namespace MonoGame.Extended.Sprites @@ -11,7 +12,7 @@ namespace MonoGame.Extended.Sprites Cycles = new Dictionary(); } - public TextureAtlas TextureAtlas { get; set; } + public Texture2DAtlas TextureAtlas { get; set; } public Dictionary Cycles { get; set; } public SpriteSheetAnimation CreateAnimation(string name) @@ -24,4 +25,4 @@ namespace MonoGame.Extended.Sprites return new SpriteSheetAnimation(name, keyFrames, cycle.FrameDuration, cycle.IsLooping, cycle.IsReversed, cycle.IsPingPong); } } -} \ No newline at end of file +} diff --git a/source/MonoGame.Extended/Sprites/SpriteSheetAnimation.cs b/source/MonoGame.Extended/Sprites/SpriteSheetAnimation.cs index 3b60e5e4..4d39db60 100644 --- a/source/MonoGame.Extended/Sprites/SpriteSheetAnimation.cs +++ b/source/MonoGame.Extended/Sprites/SpriteSheetAnimation.cs @@ -1,6 +1,6 @@ using System; using System.Linq; -using MonoGame.Extended.TextureAtlases; +using MonoGame.Extended.Graphics; namespace MonoGame.Extended.Sprites { @@ -8,13 +8,13 @@ namespace MonoGame.Extended.Sprites { public const float DefaultFrameDuration = 0.2f; - public SpriteSheetAnimation(string name, TextureAtlas textureAtlas, float frameDuration = DefaultFrameDuration, + public SpriteSheetAnimation(string name, Texture2DAtlas textureAtlas, float frameDuration = DefaultFrameDuration, bool isLooping = true, bool isReversed = false, bool isPingPong = false) - : this(name, textureAtlas.Regions.ToArray(), frameDuration, isLooping, isReversed, isPingPong) + : this(name, textureAtlas.ToArray(), frameDuration, isLooping, isReversed, isPingPong) { } - public SpriteSheetAnimation(string name, TextureRegion2D[] keyFrames, float frameDuration = DefaultFrameDuration, + public SpriteSheetAnimation(string name, Texture2DRegion[] keyFrames, float frameDuration = DefaultFrameDuration, bool isLooping = true, bool isReversed = false, bool isPingPong = false) : base(null, false) { @@ -27,13 +27,13 @@ namespace MonoGame.Extended.Sprites CurrentFrameIndex = IsReversed ? KeyFrames.Length - 1 : 0; } - public SpriteSheetAnimation(string name, TextureRegion2D[] keyFrames, SpriteSheetAnimationData data) + public SpriteSheetAnimation(string name, Texture2DRegion[] keyFrames, SpriteSheetAnimationData data) : this(name, keyFrames, data.FrameDuration, data.IsLooping, data.IsReversed, data.IsPingPong) { } public string Name { get; } - public TextureRegion2D[] KeyFrames { get; } + public Texture2DRegion[] KeyFrames { get; } public float FrameDuration { get; set; } public bool IsLooping { get; set; } public bool IsReversed { get; set; } @@ -44,7 +44,7 @@ namespace MonoGame.Extended.Sprites ? (KeyFrames.Length*2 - (IsLooping ? 2 : 1))*FrameDuration : KeyFrames.Length*FrameDuration; - public TextureRegion2D CurrentFrame => KeyFrames[CurrentFrameIndex]; + public Texture2DRegion CurrentFrame => KeyFrames[CurrentFrameIndex]; public int CurrentFrameIndex { get; private set; } public float FramesPerSecond @@ -104,4 +104,4 @@ namespace MonoGame.Extended.Sprites return IsComplete; } } -} \ No newline at end of file +} diff --git a/source/MonoGame.Extended/TextureAtlases/NinePatchRegion2D.cs b/source/MonoGame.Extended/TextureAtlases/NinePatchRegion2D.cs deleted file mode 100644 index 49323e01..00000000 --- a/source/MonoGame.Extended/TextureAtlases/NinePatchRegion2D.cs +++ /dev/null @@ -1,84 +0,0 @@ -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; - -namespace MonoGame.Extended.TextureAtlases -{ - public class NinePatchRegion2D : TextureRegion2D - { - public Rectangle[] SourcePatches { get; } = new Rectangle[9]; - public Thickness Padding { get; } - public int LeftPadding => Padding.Left; - public int TopPadding => Padding.Top; - public int RightPadding => Padding.Right; - public int BottomPadding => Padding.Bottom; - - public NinePatchRegion2D(TextureRegion2D textureRegion, Thickness padding) - : base(textureRegion.Name, textureRegion.Texture, textureRegion.X, textureRegion.Y, textureRegion.Width, textureRegion.Height) - { - Padding = padding; - CachePatches(textureRegion.Bounds, SourcePatches); - } - - public NinePatchRegion2D(TextureRegion2D textureRegion, int padding) - : this(textureRegion, padding, padding, padding, padding) - { - } - - public NinePatchRegion2D(TextureRegion2D textureRegion, int leftRightPadding, int topBottomPadding) - : this(textureRegion, leftRightPadding, topBottomPadding, leftRightPadding, topBottomPadding) - { - } - - public NinePatchRegion2D(TextureRegion2D textureRegion, int leftPadding, int topPadding, int rightPadding, int bottomPadding) - : this(textureRegion, new Thickness(leftPadding, topPadding, rightPadding, bottomPadding)) - { - } - - public NinePatchRegion2D(Texture2D texture, Thickness thickness) - : this(new TextureRegion2D(texture), thickness) - { - } - - public const int TopLeft = 0; - public const int TopMiddle = 1; - public const int TopRight = 2; - public const int MiddleLeft = 3; - public const int Middle = 4; - public const int MiddleRight = 5; - public const int BottomLeft = 6; - public const int BottomMiddle = 7; - public const int BottomRight = 8; - - private readonly Rectangle[] _destinationPatches = new Rectangle[9]; - - public Rectangle[] CreatePatches(Rectangle rectangle) - { - CachePatches(rectangle, _destinationPatches); - return _destinationPatches; - } - - private void CachePatches(Rectangle sourceRectangle, Rectangle[] patchCache) - { - var x = sourceRectangle.X; - var y = sourceRectangle.Y; - var w = sourceRectangle.Width; - var h = sourceRectangle.Height; - var middleWidth = w - LeftPadding - RightPadding; - var middleHeight = h - TopPadding - BottomPadding; - var bottomY = y + h - BottomPadding; - var rightX = x + w - RightPadding; - var leftX = x + LeftPadding; - var topY = y + TopPadding; - - patchCache[TopLeft] = new Rectangle(x, y, LeftPadding, TopPadding); - patchCache[TopMiddle] = new Rectangle(leftX, y, middleWidth, TopPadding); - patchCache[TopRight] = new Rectangle(rightX, y, RightPadding, TopPadding); - patchCache[MiddleLeft] = new Rectangle(x, topY, LeftPadding, middleHeight); - patchCache[Middle] = new Rectangle(leftX, topY, middleWidth, middleHeight); - patchCache[MiddleRight] = new Rectangle(rightX, topY, RightPadding, middleHeight); - patchCache[BottomLeft] = new Rectangle(x, bottomY, LeftPadding, BottomPadding); - patchCache[BottomMiddle] = new Rectangle(leftX, bottomY, middleWidth, BottomPadding); - patchCache[BottomRight] = new Rectangle(rightX, bottomY, RightPadding, BottomPadding); - } - } -} \ No newline at end of file diff --git a/source/MonoGame.Extended/TextureAtlases/TextureAtlas.cs b/source/MonoGame.Extended/TextureAtlases/TextureAtlas.cs deleted file mode 100644 index d9fd41d6..00000000 --- a/source/MonoGame.Extended/TextureAtlases/TextureAtlas.cs +++ /dev/null @@ -1,258 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; - -namespace MonoGame.Extended.TextureAtlases -{ - /// - /// Defines a texture atlas which stores a source image and contains regions specifying its sub-images. - /// - /// - /// - /// Texture atlas (also called a tile map, tile engine, or sprite sheet) is a large image containing a collection, - /// or "atlas", of sub-images, each of which is a texture map for some part of a 2D or 3D model. - /// The sub-textures can be rendered by modifying the texture coordinates of the object's uvmap on the atlas, - /// essentially telling it which part of the image its texture is in. - /// In an application where many small textures are used frequently, it is often more efficient to store the - /// textures in a texture atlas which is treated as a single unit by the graphics hardware. - /// This saves memory and because there are less rendering state changes by binding once, it can be faster to bind - /// one large texture once than to bind many smaller textures as they are drawn. - /// Careful alignment may be needed to avoid bleeding between sub textures when used with mipmapping, and artefacts - /// between tiles for texture compression. - /// - /// - public class TextureAtlas : IEnumerable - { - /// - /// Initializes a new texture atlas with an empty list of regions. - /// - /// The asset name of this texture atlas - /// Source image used to draw on screen. - public TextureAtlas(string name, Texture2D texture) - { - Name = name; - Texture = texture; - - _regionsByName = new Dictionary(); - _regionsByIndex = new List(); - } - - /// - /// - /// Initializes a new texture atlas and populates it with regions. - /// - /// The asset name of this texture atlas - /// Source image used to draw on screen. - /// A collection of regions to populate the atlas with. - public TextureAtlas(string name, Texture2D texture, Dictionary regions) - : this(name, texture) - { - foreach (var region in regions) - CreateRegion(region.Key, region.Value.X, region.Value.Y, region.Value.Width, region.Value.Height); - } - - private readonly Dictionary _regionsByName; - private readonly List _regionsByIndex; - - public string Name { get; } - - /// - /// Gets a source image. - /// - public Texture2D Texture { get; } - - /// - /// Gets a list of regions in the . - /// - public IEnumerable Regions => _regionsByIndex; - - /// - /// Gets the number of regions in the . - /// - public int RegionCount => _regionsByIndex.Count; - - public TextureRegion2D this[string name] => GetRegion(name); - public TextureRegion2D this[int index] => GetRegion(index); - - /// - /// Gets the enumerator of the ' list of regions. - /// - /// The of regions. - public IEnumerator GetEnumerator() - { - return _regionsByIndex.GetEnumerator(); - } - - /// - /// Gets the enumerator of the ' list of regions. - /// - /// The of regions - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - /// - /// Determines whether the texture atlas contains a region - /// - /// Name of the texture region. - /// - public bool ContainsRegion(string name) - { - return _regionsByName.ContainsKey(name); - } - - /// - /// Internal method for adding region - /// - /// Texture region. - private void AddRegion(TextureRegion2D region) - { - _regionsByIndex.Add(region); - _regionsByName.Add(region.Name, region); - } - - /// - /// Creates a new texture region and adds it to the list of the ' regions. - /// - /// Name of the texture region. - /// X coordinate of the region's top left corner. - /// Y coordinate of the region's top left corner. - /// Width of the texture region. - /// Height of the texture region. - /// Created texture region. - public TextureRegion2D CreateRegion(string name, int x, int y, int width, int height) - { - if (_regionsByName.ContainsKey(name)) - throw new InvalidOperationException($"Region {name} already exists in the texture atlas"); - - var region = new TextureRegion2D(name, Texture, x, y, width, height); - AddRegion(region); - return region; - } - - /// - /// Creates a new nine patch texture region and adds it to the list of the ' regions. - /// - /// Name of the texture region. - /// X coordinate of the region's top left corner. - /// Y coordinate of the region's top left corner. - /// Width of the texture region. - /// Height of the texture region. - /// Thickness of the nine patch region. - /// Created texture region. - public NinePatchRegion2D CreateNinePatchRegion(string name, int x, int y, int width, int height, Thickness thickness) - { - if (_regionsByName.ContainsKey(name)) - throw new InvalidOperationException($"Region {name} already exists in the texture atlas"); - - var textureRegion = new TextureRegion2D(name, Texture, x, y, width, height); - var ninePatchRegion = new NinePatchRegion2D(textureRegion, thickness); - AddRegion(ninePatchRegion); - return ninePatchRegion; - } - - /// - /// Removes a texture region from the - /// - /// An index of the in to remove - public void RemoveRegion(int index) - { - var region = _regionsByIndex[index]; - _regionsByIndex.RemoveAt(index); - - if(region.Name != null) - _regionsByName.Remove(region.Name); - } - - /// - /// Removes a texture region from the - /// - /// Name of the to remove - public void RemoveRegion(string name) - { - if (_regionsByName.TryGetValue(name, out var region)) - { - _regionsByName.Remove(name); - _regionsByIndex.Remove(region); - } - } - - /// - /// Gets a from the ' list. - /// - /// An index of the in to get. - /// The . - public TextureRegion2D GetRegion(int index) - { - if (index < 0 || index >= _regionsByIndex.Count) - throw new IndexOutOfRangeException(); - - return _regionsByIndex[index]; - } - - /// - /// Gets a from the ' list. - /// - /// Name of the to get. - /// The . - public TextureRegion2D GetRegion(string name) - { - return GetRegion(name); - } - - /// - /// Gets a texture region from the of a specified type. - /// This is can be useful if the atlas contains 's. - /// - /// Type of the region to get - /// Name of the region to get - /// The texture region - public T GetRegion(string name) where T : TextureRegion2D - { - if (_regionsByName.TryGetValue(name, out var region)) - return (T)region; - - throw new KeyNotFoundException(name); - } - - /// - /// Creates a new and populates it with a grid of . - /// - /// The name of this texture atlas - /// Source image used to draw on screen - /// Width of the . - /// Height of the . - /// The number of to create. - /// Minimum distance of the regions from the border of the source image. - /// Horizontal and vertical space between regions. - /// A created and populated . - public static TextureAtlas Create(string name, Texture2D texture, int regionWidth, int regionHeight, - int maxRegionCount = int.MaxValue, int margin = 0, int spacing = 0) - { - var textureAtlas = new TextureAtlas(name, texture); - var count = 0; - var width = texture.Width - margin; - var height = texture.Height - margin; - var xIncrement = regionWidth + spacing; - var yIncrement = regionHeight + spacing; - - for (var y = margin; y < height; y += yIncrement) - { - for (var x = margin; x < width; x += xIncrement) - { - var regionName = $"{texture.Name ?? "region"}{count}"; - textureAtlas.CreateRegion(regionName, x, y, regionWidth, regionHeight); - count++; - - if (count >= maxRegionCount) - return textureAtlas; - } - } - - return textureAtlas; - } - } -} \ No newline at end of file diff --git a/source/MonoGame.Extended/TextureAtlases/TextureAtlasExtensions.cs b/source/MonoGame.Extended/TextureAtlases/TextureAtlasExtensions.cs deleted file mode 100644 index 0ec6b86b..00000000 --- a/source/MonoGame.Extended/TextureAtlases/TextureAtlasExtensions.cs +++ /dev/null @@ -1,113 +0,0 @@ -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; - -namespace MonoGame.Extended.TextureAtlases -{ - public static class TextureAtlasExtensions - { - public static void Draw(this SpriteBatch spriteBatch, TextureRegion2D textureRegion, Vector2 position, Color color, Rectangle? clippingRectangle = null) - { - Draw(spriteBatch, textureRegion, position, color, 0, Vector2.Zero, Vector2.One, SpriteEffects.None, 0, clippingRectangle); - } - - public static void Draw(this SpriteBatch spriteBatch, TextureRegion2D textureRegion, Vector2 position, Color color, - float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth, Rectangle? clippingRectangle = null) - { - var sourceRectangle = textureRegion.Bounds; - - if (clippingRectangle.HasValue) - { - var x = (int)(position.X - origin.X); - var y = (int)(position.Y - origin.Y); - var width = (int)(textureRegion.Width * scale.X); - var height = (int)(textureRegion.Height * scale.Y); - var destinationRectangle = new Rectangle(x, y, width, height); - - sourceRectangle = ClipSourceRectangle(textureRegion.Bounds, destinationRectangle, clippingRectangle.Value); - position.X += sourceRectangle.X - textureRegion.Bounds.X; - position.Y += sourceRectangle.Y - textureRegion.Bounds.Y; - - if(sourceRectangle.Width <= 0 || sourceRectangle.Height <= 0) - return; - } - - spriteBatch.Draw(textureRegion.Texture, position, sourceRectangle, color, rotation, origin, scale, effects, layerDepth); - } - - public static void Draw(this SpriteBatch spriteBatch, TextureRegion2D textureRegion, Rectangle destinationRectangle, Color color, Rectangle? clippingRectangle = null) - { - var ninePatchRegion = textureRegion as NinePatchRegion2D; - - if (ninePatchRegion != null) - Draw(spriteBatch, ninePatchRegion, destinationRectangle, color, clippingRectangle); - else - Draw(spriteBatch, textureRegion.Texture, textureRegion.Bounds, destinationRectangle, color, clippingRectangle); - } - - public static void Draw(this SpriteBatch spriteBatch, NinePatchRegion2D ninePatchRegion, Rectangle destinationRectangle, Color color, Rectangle? clippingRectangle = null) - { - var destinationPatches = ninePatchRegion.CreatePatches(destinationRectangle); - var sourcePatches = ninePatchRegion.SourcePatches; - - for (var i = 0; i < sourcePatches.Length; i++) - { - var sourcePatch = sourcePatches[i]; - var destinationPatch = destinationPatches[i]; - - if (clippingRectangle.HasValue) - { - sourcePatch = ClipSourceRectangle(sourcePatch, destinationPatch, clippingRectangle.Value); - destinationPatch = ClipDestinationRectangle(destinationPatch, clippingRectangle.Value); - Draw(spriteBatch, ninePatchRegion.Texture, sourcePatch, destinationPatch, color, clippingRectangle); - } - else - { - if (destinationPatch.Width > 0 && destinationPatch.Height > 0) - spriteBatch.Draw(ninePatchRegion.Texture, sourceRectangle: sourcePatch, destinationRectangle: destinationPatch, color: color); - } - } - } - - public static void Draw(this SpriteBatch spriteBatch, Texture2D texture, Rectangle sourceRectangle, Rectangle destinationRectangle, Color color, Rectangle? clippingRectangle) - { - if (clippingRectangle.HasValue) - { - sourceRectangle = ClipSourceRectangle(sourceRectangle, destinationRectangle, clippingRectangle.Value); - destinationRectangle = ClipDestinationRectangle(destinationRectangle, clippingRectangle.Value); - } - - if (destinationRectangle.Width > 0 && destinationRectangle.Height > 0) - spriteBatch.Draw(texture, destinationRectangle, sourceRectangle, color); - } - - private static Rectangle ClipSourceRectangle(Rectangle sourceRectangle, Rectangle destinationRectangle, Rectangle clippingRectangle) - { - var left = (float)(clippingRectangle.Left - destinationRectangle.Left); - var right = (float)(destinationRectangle.Right - clippingRectangle.Right); - var top = (float)(clippingRectangle.Top - destinationRectangle.Top); - var bottom = (float)(destinationRectangle.Bottom - clippingRectangle.Bottom); - var x = left > 0 ? left : 0; - var y = top > 0 ? top : 0; - var w = (right > 0 ? right : 0) + x; - var h = (bottom > 0 ? bottom : 0) + y; - - var scaleX = (float)destinationRectangle.Width / sourceRectangle.Width; - var scaleY = (float)destinationRectangle.Height / sourceRectangle.Height; - x /= scaleX; - y /= scaleY; - w /= scaleX; - h /= scaleY; - - return new Rectangle((int)(sourceRectangle.X + x), (int)(sourceRectangle.Y + y), (int)(sourceRectangle.Width - w), (int)(sourceRectangle.Height - h)); - } - - private static Rectangle ClipDestinationRectangle(Rectangle destinationRectangle, Rectangle clippingRectangle) - { - var left = clippingRectangle.Left < destinationRectangle.Left ? destinationRectangle.Left : clippingRectangle.Left; - var top = clippingRectangle.Top < destinationRectangle.Top ? destinationRectangle.Top : clippingRectangle.Top; - var bottom = clippingRectangle.Bottom < destinationRectangle.Bottom ? clippingRectangle.Bottom : destinationRectangle.Bottom; - var right = clippingRectangle.Right < destinationRectangle.Right ? clippingRectangle.Right : destinationRectangle.Right; - return new Rectangle(left, top, right - left, bottom - top); - } - } -} \ No newline at end of file diff --git a/source/MonoGame.Extended/TextureAtlases/TextureRegion2D.cs b/source/MonoGame.Extended/TextureAtlases/TextureRegion2D.cs deleted file mode 100644 index f52a2097..00000000 --- a/source/MonoGame.Extended/TextureAtlases/TextureRegion2D.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; - -namespace MonoGame.Extended.TextureAtlases -{ - public class TextureRegion2D - { - public TextureRegion2D(Texture2D texture, int x, int y, int width, int height) - : this(null, texture, x, y, width, height) - { - } - - public TextureRegion2D(Texture2D texture, Rectangle region) - : this(null, texture, region.X, region.Y, region.Width, region.Height) - { - } - - public TextureRegion2D(string name, Texture2D texture, Rectangle region) - : this(name, texture, region.X, region.Y, region.Width, region.Height) - { - } - - public TextureRegion2D(Texture2D texture) - : this(texture.Name, texture, 0, 0, texture.Width, texture.Height) - { - } - - public TextureRegion2D(string name, Texture2D texture, int x, int y, int width, int height) - { - Name = name; - Texture = texture; - X = x; - Y = y; - Width = width; - Height = height; - } - - public string Name { get; } - public Texture2D Texture { get; protected set; } - public int X { get; } - public int Y { get; } - public int Width { get; } - public int Height { get; } - public SizeF Size => new SizeF(Width, Height); - public object Tag { get; set; } - public Rectangle Bounds => new Rectangle(X, Y, Width, Height); - - public override string ToString() - { - return $"{Name ?? string.Empty} {Bounds}"; - } - } -} diff --git a/tests/MonoGame.Extended.Tests/BitmapFonts/BitmapFontTests.cs b/tests/MonoGame.Extended.Tests/BitmapFonts/BitmapFontTests.cs index 6a0c0eba..1273bf5d 100644 --- a/tests/MonoGame.Extended.Tests/BitmapFonts/BitmapFontTests.cs +++ b/tests/MonoGame.Extended.Tests/BitmapFonts/BitmapFontTests.cs @@ -1,6 +1,7 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using MonoGame.Extended.BitmapFonts; +using MonoGame.Extended.Graphics; using MonoGame.Extended.TextureAtlases; using Xunit; @@ -78,7 +79,7 @@ namespace MonoGame.Extended.Tests.BitmapFonts private static BitmapFont CreateTestFont() { - var textureRegion = new TextureRegion2D(null, x: 219, y: 61, width: 16, height: 18); + var textureRegion = new Texture2DRegion("Test Font", x: 219, y: 61, width: 16, height: 18); var regions = new[] { // extracted from 'Impact' font. 'x' is particularly interesting because it has a negative x offset diff --git a/tests/MonoGame.Extended.Tests/Sprites/SpriteSheetAnimationTests.cs b/tests/MonoGame.Extended.Tests/Sprites/SpriteSheetAnimationTests.cs index 35ad3e3d..7a9bc372 100644 --- a/tests/MonoGame.Extended.Tests/Sprites/SpriteSheetAnimationTests.cs +++ b/tests/MonoGame.Extended.Tests/Sprites/SpriteSheetAnimationTests.cs @@ -1,5 +1,6 @@ using System; using Microsoft.Xna.Framework; +using MonoGame.Extended.Graphics; using MonoGame.Extended.Sprites; using MonoGame.Extended.TextureAtlases; using Xunit; @@ -20,8 +21,8 @@ namespace MonoGame.Extended.Tests.Sprites [InlineData(1, 5f)] public void Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Not_Complete(int expectedTextureRegionIndex, float time) { - var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); - var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); + var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; @@ -48,8 +49,8 @@ namespace MonoGame.Extended.Tests.Sprites [Fact] public void Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Not_Complete_Over_Multiple_Updates() { - var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); - var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); + var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; @@ -100,8 +101,8 @@ namespace MonoGame.Extended.Tests.Sprites [InlineData(1, 1.9f)] public void Non_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Not_Complete(int expectedTextureRegionIndex, float time) { - var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); - var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); + var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; @@ -131,8 +132,8 @@ namespace MonoGame.Extended.Tests.Sprites [InlineData(1, 4f)] public void Non_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Complete_When_AnimationDuration_Is_Reached(int expectedTextureRegionIndex, float time) { - var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); - var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); + var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; @@ -159,8 +160,8 @@ namespace MonoGame.Extended.Tests.Sprites [Fact] public void Non_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Complete_When_AnimationDuration_Is_Reached_Over_Multiple_Updates() { - var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); - var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); + var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; @@ -218,8 +219,8 @@ namespace MonoGame.Extended.Tests.Sprites [InlineData(0, 5f)] public void Reversed_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Not_Complete(int expectedTextureRegionIndex, float time) { - var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); - var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); + var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; @@ -247,8 +248,8 @@ namespace MonoGame.Extended.Tests.Sprites [Fact] public void Reversed_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Not_Complete_Over_Multiple_Updates() { - var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); - var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); + var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; @@ -300,8 +301,8 @@ namespace MonoGame.Extended.Tests.Sprites [InlineData(0, 1.9f)] public void Reversed_Non_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Not_Complete(int expectedTextureRegionIndex, float time) { - var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); - var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); + var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; @@ -332,8 +333,8 @@ namespace MonoGame.Extended.Tests.Sprites [InlineData(0, 4f)] public void Reversed_Non_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Complete_When_AnimationDuration_Is_Reached(int expectedTextureRegionIndex, float time) { - var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); - var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); + var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; @@ -361,8 +362,8 @@ namespace MonoGame.Extended.Tests.Sprites [Fact] public void Reversed_Non_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Complete_When_AnimationDuration_Is_Reached_Over_Multiple_Updates() { - var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); - var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); + var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; @@ -424,8 +425,8 @@ namespace MonoGame.Extended.Tests.Sprites [InlineData(0, 8f)] public void PingPong_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Not_Complete(int expectedTextureRegionIndex, float time) { - var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); - var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); + var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; @@ -454,8 +455,8 @@ namespace MonoGame.Extended.Tests.Sprites [Fact] public void PingPong_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Not_Complete_Over_Multiple_Updates() { - var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); - var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); + var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; @@ -523,8 +524,8 @@ namespace MonoGame.Extended.Tests.Sprites [InlineData(0, 2.9f)] public void PingPong_Non_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Not_Complete(int expectedTextureRegionIndex, float time) { - var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); - var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); + var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; @@ -556,8 +557,8 @@ namespace MonoGame.Extended.Tests.Sprites [InlineData(0, 5f)] public void PingPong_Non_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Complete_When_AnimationDuration_Is_Reached(int expectedTextureRegionIndex, float time) { - var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); - var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); + var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; @@ -586,9 +587,9 @@ namespace MonoGame.Extended.Tests.Sprites [Fact] public void PingPong_Non_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Complete_When_AnimationDuration_Is_Reached_Over_Multiple_Updates() { - var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); - var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); - var textureRegion2D3 = new TextureRegion2D("Region 3", null, new Rectangle()); + var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); + var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); + var textureRegion2D3 = new Texture2DRegion("Region 3", new Rectangle()); var textureRegions = new[] { textureRegion2D1, textureRegion2D2, textureRegion2D3 }; @@ -673,8 +674,8 @@ namespace MonoGame.Extended.Tests.Sprites [InlineData(1, 8f)] public void Reversed_PingPong_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Not_Complete(int expectedTextureRegionIndex, float time) { - var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); - var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); + var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; @@ -703,8 +704,8 @@ namespace MonoGame.Extended.Tests.Sprites [Fact] public void Reversed_PingPong_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Not_Complete_Over_Multiple_Updates() { - var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); - var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); + var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; @@ -772,8 +773,8 @@ namespace MonoGame.Extended.Tests.Sprites [InlineData(1, 2.9f)] public void Reversed_PingPong_Non_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Not_Complete(int expectedTextureRegionIndex, float time) { - var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); - var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); + var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; @@ -805,8 +806,8 @@ namespace MonoGame.Extended.Tests.Sprites [InlineData(1, 5f)] public void Reversed_PingPong_Non_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Complete_When_AnimationDuration_Is_Reached(int expectedTextureRegionIndex, float time) { - var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); - var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); + var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; @@ -835,9 +836,9 @@ namespace MonoGame.Extended.Tests.Sprites [Fact] public void Reversed_PingPong_Non_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Complete_When_AnimationDuration_Is_Reached_Over_Multiple_Updates() { - var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); - var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); - var textureRegion2D3 = new TextureRegion2D("Region 3", null, new Rectangle()); + var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); + var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); + var textureRegion2D3 = new Texture2DRegion("Region 3", new Rectangle()); var textureRegions = new[] { textureRegion2D1, textureRegion2D2, textureRegion2D3 };