From 043508ed4264a3024b281665bfbf6e073c029fdf Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Fri, 3 Mar 2017 20:22:30 -0500 Subject: add texture patching to content events (#173) --- .../Framework/ContentEventHelper.cs | 51 ++++++++++++++++++++++ src/StardewModdingAPI/IContentEventHelper.cs | 12 +++++ src/StardewModdingAPI/PatchMode.cs | 12 +++++ src/StardewModdingAPI/StardewModdingAPI.csproj | 1 + 4 files changed, 76 insertions(+) create mode 100644 src/StardewModdingAPI/PatchMode.cs (limited to 'src/StardewModdingAPI') diff --git a/src/StardewModdingAPI/Framework/ContentEventHelper.cs b/src/StardewModdingAPI/Framework/ContentEventHelper.cs index a58efe32..ebd04692 100644 --- a/src/StardewModdingAPI/Framework/ContentEventHelper.cs +++ b/src/StardewModdingAPI/Framework/ContentEventHelper.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace StardewModdingAPI.Framework @@ -85,6 +86,56 @@ namespace StardewModdingAPI.Framework data[key] = value(data[key]); } + /// Overwrite part of the image. + /// The image to patch into the content. + /// The part of the to copy (or null to take the whole texture). This must be within the bounds of the texture. + /// The part of the content to patch (or null to patch the whole texture). The original content within this area will be erased. This must be within the bounds of the existing spritesheet. + /// Indicates how an image should be patched. + /// One of the arguments is null. + /// The is outside the bounds of the spritesheet. + /// The content being read isn't an image. + public void PatchImage(Texture2D source, Rectangle? sourceArea = null, Rectangle? targetArea = null, PatchMode patchMode = PatchMode.Replace) + { + // get texture + Texture2D target = this.GetData(); + + // get areas + sourceArea = sourceArea ?? new Rectangle(0, 0, source.Width, source.Height); + targetArea = targetArea ?? new Rectangle(0, 0, Math.Min(sourceArea.Value.Width, target.Width), Math.Min(sourceArea.Value.Height, target.Height)); + + // validate + if (source == null) + throw new ArgumentNullException(nameof(source), "Can't patch from a null source texture."); + if (sourceArea.Value.X < 0 || sourceArea.Value.Y < 0 || sourceArea.Value.Right > source.Width || sourceArea.Value.Bottom > source.Height) + throw new ArgumentOutOfRangeException(nameof(sourceArea), "The source area is outside the bounds of the source texture."); + if (targetArea.Value.X < 0 || targetArea.Value.Y < 0 || targetArea.Value.Right > target.Width || targetArea.Value.Bottom > target.Height) + throw new ArgumentOutOfRangeException(nameof(targetArea), "The target area is outside the bounds of the target texture."); + if (sourceArea.Value.Width != targetArea.Value.Width || sourceArea.Value.Height != targetArea.Value.Height) + throw new InvalidOperationException("The source and target areas must be the same size."); + + // get source data + int pixelCount = sourceArea.Value.Width * sourceArea.Value.Height; + Color[] sourceData = new Color[pixelCount]; + source.GetData(0, sourceArea, sourceData, 0, pixelCount); + + // merge data in overlay mode + if (patchMode == PatchMode.Overlay) + { + Color[] newData = new Color[targetArea.Value.Width * targetArea.Value.Height]; + target.GetData(0, targetArea, newData, 0, newData.Length); + for (int i = 0; i < sourceData.Length; i++) + { + Color pixel = sourceData[i]; + if (pixel.A != 0) // not transparent + newData[i] = pixel; + } + sourceData = newData; + } + + // patch target texture + target.SetData(0, targetArea, sourceData, 0, pixelCount); + } + /// Replace the entire content value with the given value. This is generally not recommended, since it may break compatibility with other mods or different versions of the game. /// The new content value. /// The is null. diff --git a/src/StardewModdingAPI/IContentEventHelper.cs b/src/StardewModdingAPI/IContentEventHelper.cs index be8290eb..7b8fd3bc 100644 --- a/src/StardewModdingAPI/IContentEventHelper.cs +++ b/src/StardewModdingAPI/IContentEventHelper.cs @@ -1,4 +1,6 @@ using System; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; namespace StardewModdingAPI { @@ -46,6 +48,16 @@ namespace StardewModdingAPI /// The content being read isn't a dictionary. void SetDictionaryEntry(TKey key, Func value); + /// Overwrite part of the image. + /// The image to patch into the content. + /// The part of the to copy (or null to take the whole texture). This must be within the bounds of the texture. + /// The part of the content to patch (or null to patch the whole texture). The original content within this area will be erased. This must be within the bounds of the existing spritesheet. + /// Indicates how an image should be patched. + /// One of the arguments is null. + /// The is outside the bounds of the spritesheet. + /// The content being read isn't an image. + void PatchImage(Texture2D source, Rectangle? sourceArea = null, Rectangle? targetArea = null, PatchMode patchMode = PatchMode.Replace); + /// Replace the entire content value with the given value. This is generally not recommended, since it may break compatibility with other mods or different versions of the game. /// The new content value. /// The is null. diff --git a/src/StardewModdingAPI/PatchMode.cs b/src/StardewModdingAPI/PatchMode.cs new file mode 100644 index 00000000..b4286a89 --- /dev/null +++ b/src/StardewModdingAPI/PatchMode.cs @@ -0,0 +1,12 @@ +namespace StardewModdingAPI +{ + /// Indicates how an image should be patched. + public enum PatchMode + { + /// Erase the original content within the area before drawing the new content. + Replace, + + /// Draw the new content over the original content, so the original content shows through any transparent pixels. + Overlay + } +} diff --git a/src/StardewModdingAPI/StardewModdingAPI.csproj b/src/StardewModdingAPI/StardewModdingAPI.csproj index 4d782f56..40f964b9 100644 --- a/src/StardewModdingAPI/StardewModdingAPI.csproj +++ b/src/StardewModdingAPI/StardewModdingAPI.csproj @@ -196,6 +196,7 @@ + -- cgit