public void Edit <T> (IAssetData asset) { IAssetDataForImage editor = asset.AsImage(); editor.ExtendImage(16, 64); editor.PatchImage(icon, targetArea: new Rectangle(0, 48, 16, 16)); }
/// <summary>Apply the patch to an asset.</summary> /// <typeparam name="T">The asset type.</typeparam> /// <param name="asset">The asset to edit.</param> public void Apply <T>(IAssetData asset) { // validate if (typeof(T) != typeof(Texture2D)) { this.Monitor.Log($"Can't apply edit-image patch by {this.ContentPack.Manifest.Name} to {this.AssetName}: this file isn't an image file (found {typeof(T)}).", LogLevel.Warn); return; } // fetch data Texture2D source = this.AssetLoader.Load <Texture2D>(this.ContentPack, this.FromLocalAsset); IAssetDataForImage editor = asset.AsImage(); // extend tilesheet if needed Rectangle affectedArea = this.GetTargetArea(source, this.FromArea, this.ToArea); if (affectedArea.Bottom > editor.Data.Height) { Texture2D original = editor.Data; Texture2D texture = new Texture2D(Game1.graphics.GraphicsDevice, original.Width, affectedArea.Bottom); editor.ReplaceWith(texture); editor.PatchImage(original); } // apply source image editor.PatchImage(source, this.FromArea, this.ToArea); }
/// <summary> /// Patches SpaceCore extended tilesheets. /// </summary> /// <param name="asset"> The asset to change.</param> /// <param name="sourceTexture"> The source texture.</param> /// <param name="sourceRect"> Source Rectangle of source texture.</param> /// <param name="targetRect"> Source Rectangle of target texture.</param> public void SpaceCorePatchExtendedTileSheet(IAssetDataForImage asset, Texture2D sourceTexture, Rectangle sourceRect, Rectangle targetRect) { Assembly spaceCoreAssembly = GetSpaceCoreAssembly(); MethodInfo patchExtendedTileSheet = spaceCoreAssembly.GetType("SpaceCore.TileSheetExtensions").GetMethod("PatchExtendedTileSheet"); patchExtendedTileSheet.Invoke(null, new object[] { asset, sourceTexture, sourceRect, targetRect, PatchMode.Replace }); }
/// <summary>Implements <see cref="IAssetEditor.Edit"/>.</summary> public void Edit <T>(IAssetData asset) { if (asset.AssetNameEquals("Characters/Haley") || asset.AssetNameEquals("Portraits/Haley")) { this.Monitor.Log($"Edit asset {asset.AssetName}"); IAssetDataForImage baseImage = asset.AsImage(); // Support for Cold Wether Haley. Texture2D overlay = hasColdWeatherHaley_ && Game1.IsWinter ? this.Helper.Content.Load <Texture2D>($"assets/{asset.AssetName}_winter_overlay_hair_gray.png") : this.Helper.Content.Load <Texture2D>($"assets/{asset.AssetName}_overlay_hair_gray.png"); // Workaround for the missing sleeping sprite of Cold Weather Haley. if (hasColdWeatherHaley_ && Game1.IsWinter && asset.AssetNameEquals("Characters/Haley")) { Texture2D sleepingHaley = this.Helper.Content.Load <Texture2D>($"assets/{asset.AssetName}_sleeping.png"); baseImage.PatchImage(sleepingHaley, patchMode: PatchMode.Overlay); } baseImage.PatchImage(ColorBlend(overlay, actualHairColor_), patchMode: PatchMode.Overlay); } else { throw new ArgumentException($"Invalid asset {asset.AssetName}"); } }
private void ExpandSpriteSheet(IAssetDataForImage assetImage) { _monitor.VerboseLog("Expanding sprite sheet."); var spriteSheet = assetImage.Data; _monitor.VerboseLog($"Width: {spriteSheet.Width}, Height: {spriteSheet.Height}"); if (spriteSheet.Height >= NewHeight) { return; } var originalSize = spriteSheet.Width * spriteSheet.Height; var data = new Color[originalSize]; spriteSheet.GetData(data); var originalRect = new Rectangle(0, 0, spriteSheet.Width, spriteSheet.Height); var expandedSpriteSheet = new Texture2D(Game1.graphics.GraphicsDevice, spriteSheet.Width, NewHeight); _monitor.VerboseLog($"New width: {expandedSpriteSheet.Width}, New height: {expandedSpriteSheet.Height}"); expandedSpriteSheet.SetData(0, originalRect, data, 0, originalSize); try { assetImage.ReplaceWith(expandedSpriteSheet); } catch (Exception ex) { _monitor.Log("Error while expanding bigCraftableSpriteSheet: " + ex.Message); } }
private static void UpdateCDForm(IAssetData asset) { IAssetDataForImage cdForm = asset.AsImage(); Dictionary <string, string> bundlePrices = GetCurrentBundlePrices(); foreach (KeyValuePair <string, string> pair in bundlePrices) { // get number of digits in value int numDigits = pair.Value.Length; Rectangle sourceRect, destRect; Vector2 destStartPos = GetDestStartPos(pair.Key); // position on form to start overwriting from int digit, currentPos; // need to erase existing numbers if (numDigits < 5) { // start from 5th position currentPos = 5; // get dots from cdFont.png sourceRect = new Rectangle(70, 0, 35, 11); // get ten-thousands position of destination - slightly different positions on left and right side of form int offset = (pair.Key == "minecarts" || pair.Key == "greenhouse") ? 34 : 33; // get position to overwrite using offset destRect = new Rectangle((int)destStartPos.X - offset, (int)destStartPos.Y, 35, 11); // overwrite with dots cdForm.PatchImage(cdFont, sourceRect, destRect); } // start at last index in pair.Value; decrement until at first index for (int k = pair.Value.Length - 1; k >= 0; k--) { // if number is negative, need to set position of '-' manually on tileset if (pair.Value[k] == '-') { digit = 15; } // otherwise convert to int from 0-9 to find position on tileset else { // isolate current digit, convert to int digit = int.Parse(pair.Value[k].ToString()); } // get current digit position in number currentPos = numDigits - (k + 1); // create source rectangle by adjusting starting x position by tileset position determined earlier sourceRect = new Rectangle(7 * digit, 0, 7, 11); // get destination rectangle by adjusting start position by current digit position destRect = new Rectangle((int)destStartPos.X - (7 * currentPos), (int)destStartPos.Y, 7, 11); // overwrite current position with digit cdForm.PatchImage(cdFont, sourceRect, destRect); } } }
public void Edit <_T> (IAssetData asset) { IAssetDataForImage editor = asset.AsImage(); editor.PatchImage(cursor, targetArea: Game1.getSourceRectForStandardTileSheet(editor.Data, TilePosition, 16, 16)); }
public void Edit <T>(IAssetData asset) { IAssetDataForImage imageAsset = asset.AsImage(); imageAsset.PatchImage(this.cursor, targetArea: Game1.getSourceRectForStandardTileSheet( imageAsset.Data, TILE_POSITION, 16, 16)); }
/// <summary>Implements <see cref="IAssetEditor.Edit"/>.</summary> public void Edit <T>(IAssetData asset) { if (asset.AssetNameEquals("Characters/Haley") || asset.AssetNameEquals("Portraits/Haley")) { this.Monitor.Log($"Edit asset {asset.AssetName}"); IAssetDataForImage baseImage = asset.AsImage(); Texture2D overlay; // Support for Cold Weather Haley. if (hasColdWeatherHaley_ && Game1.IsWinter) { overlay = this.Helper.Content.Load <Texture2D>($"assets/{asset.AssetName}_winter_overlay_hair_gray.png"); // Workaround for the missing sleeping sprite of Cold Weather Haley. if (asset.AssetNameEquals("Characters/Haley")) { Texture2D sleepingHaley = this.Helper.Content.Load <Texture2D>($"assets/{asset.AssetName}_sleeping.png"); baseImage.PatchImage(sleepingHaley, patchMode: PatchMode.Overlay); } } // Support for RandomFlowerQueen. else if (hasRandomFlowerQueen_ && asset.AssetNameEquals("Characters/Haley")) { // We must replace the flowerqueen part of the base image since it contains unwanted pixels. Texture2D haleyNoFlowercrown = this.Helper.Content.Load <Texture2D>($"assets/{asset.AssetName}_no_flowercrown.png"); baseImage.PatchImage(haleyNoFlowercrown, targetArea: new Rectangle(0, 320, 64, 64), patchMode: PatchMode.Replace); overlay = this.Helper.Content.Load <Texture2D>($"assets/{asset.AssetName}_no_flowercrown_overlay_hair_gray.png"); } else { overlay = this.Helper.Content.Load <Texture2D>($"assets/{asset.AssetName}_overlay_hair_gray.png"); } baseImage.PatchImage(ColorBlend(overlay, actualHairColor_), patchMode: PatchMode.Overlay); } else if (asset.AssetNameEquals("Characters/Haley_Beach") || asset.AssetNameEquals("Portraits/Haley_Beach")) { this.Monitor.Log($"Edit asset {asset.AssetName}"); IAssetDataForImage baseImage = asset.AsImage(); Texture2D overlay = this.Helper.Content.Load <Texture2D>($"assets/{asset.AssetName}_overlay_hair_gray.png"); baseImage.PatchImage(ColorBlend(overlay, actualHairColor_), patchMode: PatchMode.Overlay); } else if (asset.AssetNameEquals("LooseSprites/cowPhotos") || asset.AssetNameEquals("LooseSprites/cowPhotosWinter")) { this.Monitor.Log($"Edit asset {asset.AssetName}"); IAssetDataForImage baseImage = asset.AsImage(); Texture2D overlay = this.Helper.Content.Load <Texture2D>("assets/Characters/Haley_cowPhotos_overlay_hair_gray.png"); baseImage.PatchImage(ColorBlend(overlay, actualHairColor_), patchMode: PatchMode.Overlay); } else { throw new ArgumentException($"Invalid asset {asset.AssetName}"); } }
public void Edit <T>(IAssetData data) { IAssetDataForImage img = data.AsImage(); foreach (TextureInjectorInfo entry in TextureInjector.Map[data.AssetName]) { img.PatchImage(entry.Texture, entry.Source, entry.Destination, PatchMode.Replace); } }
public void SpaceCorePatchExtendedTileSheet(IAssetDataForImage asset, Texture2D sourceTexture, Rectangle sourceRect, Rectangle targetRect) { var modData = Helper.ModRegistry.Get("spacechase0.SpaceCore"); var spaceCoreInstance = modData.GetType().GetProperty("Mod", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public).GetValue(modData); var spaceCoreAssembly = spaceCoreInstance.GetType().Assembly; var patchExtendedTileSheet = spaceCoreAssembly.GetType("SpaceCore.TileSheetExtensions").GetMethod("PatchExtendedTileSheet"); patchExtendedTileSheet.Invoke(null, new object[] { asset, sourceTexture, sourceRect, targetRect, PatchMode.Replace }); }
/// <summary>Apply the patch to a loaded asset.</summary> /// <typeparam name="T">The asset type.</typeparam> /// <param name="asset">The asset to edit.</param> public override void Edit <T>(IAssetData asset) { // validate if (typeof(T) != typeof(Texture2D)) { this.Monitor.Log($"Can't apply image patch \"{this.LogName}\" to {this.TargetAsset}: this file isn't an image file (found {typeof(T)}).", LogLevel.Warn); return; } if (!this.FromAssetExists()) { this.Monitor.Log($"Can't apply image patch \"{this.LogName}\" to {this.TargetAsset}: the {nameof(PatchConfig.FromFile)} file '{this.FromAsset}' doesn't exist.", LogLevel.Warn); return; } // fetch data Texture2D source = this.ContentPack.Load <Texture2D>(this.FromAsset); Rectangle sourceArea = this.FromArea ?? new Rectangle(0, 0, source.Width, source.Height); Rectangle targetArea = this.ToArea ?? new Rectangle(0, 0, sourceArea.Width, sourceArea.Height); IAssetDataForImage editor = asset.AsImage(); // validate error conditions if (sourceArea.X < 0 || sourceArea.Y < 0 || sourceArea.Width < 0 || sourceArea.Height < 0) { this.Monitor.Log($"Can't apply image patch \"{this.LogName}\": source area (X:{sourceArea.X}, Y:{sourceArea.Y}, Width:{sourceArea.Width}, Height:{sourceArea.Height}) has negative values, which isn't valid.", LogLevel.Error); return; } if (targetArea.X < 0 || targetArea.Y < 0 || targetArea.Width < 0 || targetArea.Height < 0) { this.Monitor.Log($"Can't apply image patch \"{this.LogName}\": target area (X:{targetArea.X}, Y:{targetArea.Y}, Width:{targetArea.Width}, Height:{targetArea.Height}) has negative values, which isn't valid.", LogLevel.Error); return; } if (targetArea.Right > editor.Data.Width) { this.Monitor.Log($"Can't apply image patch \"{this.LogName}\": target area (X:{targetArea.X}, Y:{targetArea.Y}, Width:{targetArea.Width}, Height:{targetArea.Height}) extends past the right edge of the image (Width:{editor.Data.Width}), which isn't allowed. Patches can only extend the tilesheet downwards.", LogLevel.Error); return; } if (sourceArea.Width != targetArea.Width || sourceArea.Height != targetArea.Height) { string sourceAreaLabel = this.FromArea.HasValue ? $"{nameof(this.FromArea)}" : "source image"; string targetAreaLabel = this.ToArea.HasValue ? $"{nameof(this.ToArea)}" : "target image"; this.Monitor.Log($"Can't apply image patch \"{this.LogName}\": {sourceAreaLabel} size (Width:{sourceArea.Width}, Height:{sourceArea.Height}) doesn't match {targetAreaLabel} size (Width:{targetArea.Width}, Height:{targetArea.Height}).", LogLevel.Error); return; } // extend tilesheet if needed if (targetArea.Bottom > editor.Data.Height) { Texture2D original = editor.Data; Texture2D texture = new Texture2D(Game1.graphics.GraphicsDevice, original.Width, targetArea.Bottom); editor.ReplaceWith(texture); editor.PatchImage(original); } // apply source image editor.PatchImage(source, sourceArea, this.ToArea, this.PatchMode); }
public static bool PatchImage(IAssetDataForImage __instance, ref Texture2D source, ref Rectangle?sourceArea, Rectangle?targetArea, PatchMode patchMode) { var a = new Rectangle(0, 0, __instance.Data.Width, __instance.Data.Height); var s = new Rectangle(0, 0, source.Width, source.Height); var sr = !sourceArea.HasValue ? s : sourceArea.Value; var tr = !targetArea.HasValue ? sr : targetArea.Value; if (source is ScaledTexture2D scaled) { if (a == tr && patchMode == PatchMode.Replace) { __instance.ReplaceWith(source); return(true); } if (patchMode == PatchMode.Overlay) { scaled.AsOverlay = true; } if (scaled.AsOverlay) { Color[] data = new Color[(int)(tr.Width) * (int)(tr.Height)]; __instance.Data.getArea(tr).GetData(data); scaled.SetData <Color>(data); } if (__instance.Data is MappedTexture2D map) { map.Set(tr, source); } else { __instance.ReplaceWith(new MappedTexture2D(__instance.Data, new Dictionary <Rectangle?, Texture2D>() { { tr, source } })); } } else if (__instance.Data is MappedTexture2D map) { map.Set(tr, (sr.Width != source.Width || sr.Height != source.Height) ? source.getArea(sr) : source); return(false); } else if (__instance.Data is ScaledTexture2D sc) { __instance.ReplaceWith(new MappedTexture2D(__instance.Data, new Dictionary <Rectangle?, Texture2D>() { { tr, (sr.Width != source.Width || sr.Height != source.Height) ? source.getArea(sr) : source } })); return(false); } return(true); }
private void PatchSprite(IAssetDataForImage assetImage, CustomChest customChest) { var sprite = _modHelper.Content.Load <Texture2D>(customChest.Config.SpritePath); var destinationRect = Game1.getSourceRectForStandardTileSheet(Game1.bigCraftableSpriteSheet, customChest.ParentSheetIndex, 16, 32); var sourceRect = new Rectangle(0, 0, 16, 32); _monitor.VerboseLog($"Destination rect: ({destinationRect.X}, {destinationRect.Y}) - ({destinationRect.Width}, {destinationRect.Height})"); try { assetImage.PatchImage(sprite, targetArea: destinationRect, sourceArea: sourceRect); } catch (Exception ex) { _monitor.Log("Error while patching bigCraftableSpriteSheet: " + ex.Message); } }
public void Edit <T>(IAssetData asset) { IAssetDataForImage image = asset.AsImage(); string name = Normalize(asset.AssetName) + ".4x"; if (!ScaledAssets.ContainsKey(name)) { ScaledAssets[name] = new Texture2DWrapper(image.Data, 4, name); } Texture2DWrapper wrapper = ScaledAssets[name]; if (wrapper.Locale != asset.Locale) { Monitor.Log($"Upscaling {asset.AssetName} (Locale: {asset.Locale})"); wrapper.Wrapped = Texture2DWrapper.ScaleUp(image.Data, wrapper.Scale); wrapper.Locale = asset.Locale; } image.ReplaceWith(wrapper); Helper.Content.Load <Texture2D>(wrapper.Path, ContentSource.GameContent); }
public static bool PatchImage(IAssetDataForImage __instance, Texture2D source, Rectangle?sourceArea, Rectangle?targetArea, PatchMode patchMode) { if (__instance.Data is Texture2DWrapper wrapper) { if (sourceArea.HasValue) { source = Texture2DWrapper.Crop(source, sourceArea.Value); } source = Texture2DWrapper.ScaleUp(source, wrapper.Scale); if (targetArea.HasValue) { targetArea = Texture2DWrapper.MultiplyRect(targetArea.Value, wrapper.Scale); } PropertyInfo Data = __instance.GetType().GetProperty("Data"); Data.SetValue(__instance, wrapper.Wrapped); __instance.PatchImage(source, null, targetArea, patchMode); Data.SetValue(__instance, wrapper); return(false); } return(true); }
public static void PatchExtendedTileSheet(this IAssetDataForImage asset, Texture2D source, Rectangle?sourceArea = null, Rectangle?targetArea = null, PatchMode patchMode = PatchMode.Replace) { string assetName = asset.AssetName.Replace('/', '\\'); if (!extendedTextureAssets.ContainsKey(assetName) || !targetArea.HasValue) { asset.PatchImage(source, sourceArea, targetArea, patchMode); return; } extendedTextures.Remove(extendedTextureAssets[assetName].BaseTileSheet); extendedTextureAssets[assetName].BaseTileSheet = asset.Data; extendedTextures.Add(extendedTextureAssets[assetName].BaseTileSheet, extendedTextureAssets[assetName]); var adjustedTarget = GetAdjustedTileSheetTarget(asset.Data, targetArea.Value); //Log.trace("Tilesheet target:" + adjustedTarget.TileSheet + " " + adjustedTarget.Y); if (adjustedTarget.TileSheet == 0) { asset.PatchImage(source, sourceArea, targetArea, patchMode); return; } // Cheaty hack so I don't have to reimplement patch var oldData = asset.Data; var dataProp = asset.GetType().GetProperty("Data"); try { dataProp.SetValue(asset, GetTileSheet(oldData, adjustedTarget.TileSheet)); Rectangle r = targetArea.Value; r.Y = adjustedTarget.Y; //Log.trace($"Ext-patching on {assetName}={extendedTextureAssets[assetName].AssetPath}: {r}/{asset.Data.Width}x{asset.Data.Height}"); asset.PatchImage(source, sourceArea, r, patchMode); } finally { dataProp.SetValue(asset, oldData); } }
/// <inheritdoc /> public override void Edit <T>(IAssetData asset) { // validate if (typeof(T) != typeof(Texture2D)) { this.Warn($"this file isn't an image file (found {typeof(T)})."); return; } if (!this.FromAssetExists()) { this.Warn($"the {nameof(PatchConfig.FromFile)} file '{this.FromAsset}' doesn't exist."); return; } // get editor IAssetDataForImage editor = asset.AsImage(); // fetch data this.LoadSourceImage(this.FromAsset, out int sourceWidth, out int sourceHeight, out IRawTextureData? rawSource, out Texture2D? fullSource); if (!this.TryReadArea(this.FromArea, 0, 0, sourceWidth, sourceHeight, out Rectangle sourceArea, out string?error)) { this.Warn($"the source area is invalid: {error}."); return; } if (!this.TryReadArea(this.ToArea, 0, 0, sourceArea.Width, sourceArea.Height, out Rectangle targetArea, out error)) { this.Warn($"the target area is invalid: {error}."); return; } // validate error conditions if (sourceArea.X < 0 || sourceArea.Y < 0 || sourceArea.Width < 0 || sourceArea.Height < 0) { this.Warn($"source area (X:{sourceArea.X}, Y:{sourceArea.Y}, Width:{sourceArea.Width}, Height:{sourceArea.Height}) has negative values, which isn't valid.", LogLevel.Error); return; } if (targetArea.X < 0 || targetArea.Y < 0 || targetArea.Width < 0 || targetArea.Height < 0) { this.Warn($"target area (X:{targetArea.X}, Y:{targetArea.Y}, Width:{targetArea.Width}, Height:{targetArea.Height}) has negative values, which isn't valid.", LogLevel.Error); return; } if (targetArea.Right > editor.Data.Width) { this.Warn($"target area (X:{targetArea.X}, Y:{targetArea.Y}, Width:{targetArea.Width}, Height:{targetArea.Height}) extends past the right edge of the image (Width:{editor.Data.Width}), which isn't allowed. Patches can only extend the tilesheet downwards.", LogLevel.Error); return; } if (sourceArea.Width != targetArea.Width || sourceArea.Height != targetArea.Height) { string sourceAreaLabel = this.FromArea != null ? $"{nameof(this.FromArea)}" : "source image"; string targetAreaLabel = this.ToArea != null ? $"{nameof(this.ToArea)}" : "target image"; this.Warn($"{sourceAreaLabel} size (Width:{sourceArea.Width}, Height:{sourceArea.Height}) doesn't match {targetAreaLabel} size (Width:{targetArea.Width}, Height:{targetArea.Height}).", LogLevel.Error); return; } // extend tilesheet if needed this.ResizedLastImage = editor.ExtendImage(editor.Data.Width, targetArea.Bottom); // apply source image if (rawSource is not null) { editor.PatchImage(rawSource, sourceArea, targetArea, (PatchMode)this.PatchMode); } else { editor.PatchImage(fullSource !, sourceArea, targetArea, (PatchMode)this.PatchMode); } }
public static bool ExtendImage(IAssetDataForImage __instance, int minWidth, int minHeight) { return(!(__instance.Data is ScaledTexture2D || __instance.Data is MappedTexture2D)); }
public void Edit <_T> (IAssetData asset) { IAssetDataForImage editor = asset.AsImage(); editor.PatchImage(cursor, targetArea: new Rectangle(112, 0, 16, 16)); }
public static bool PatchImage(IAssetDataForImage __instance, XTexture2D source, XRectangle?sourceArea, XRectangle?targetArea, PatchMode patchMode) { if (!Config.SMAPI.ApplyPatchEnabled) { return(true); } // get texture if (source is null) { throw new ArgumentNullException(nameof(source), "Can't patch from a null source texture."); } XTexture2D target = __instance.Data; // get areas sourceArea ??= new(0, 0, source.Width, source.Height); targetArea ??= new(0, 0, Math.Min(sourceArea.Value.Width, target.Width), Math.Min(sourceArea.Value.Height, target.Height)); // validate if (!source.Bounds.Contains(sourceArea.Value)) { throw new ArgumentOutOfRangeException(nameof(sourceArea), "The source area is outside the bounds of the source texture."); } if (!target.Bounds.Contains(targetArea.Value)) { throw new ArgumentOutOfRangeException(nameof(targetArea), "The target area is outside the bounds of the target texture."); } if (sourceArea.Value.Size != targetArea.Value.Size) { throw new InvalidOperationException("The source and target areas must be the same size."); } if (GL.Texture2DExt.CopyTexture(source, sourceArea.Value, target, targetArea.Value, patchMode)) { return(false); } // get source data int pixelCount = sourceArea.Value.Width * sourceArea.Value.Height; var sourceData = GC.AllocateUninitializedArray <XColor>(pixelCount); source.GetData(0, sourceArea, sourceData, 0, pixelCount); // merge data in overlay mode if (patchMode == PatchMode.Overlay) { // get target data var targetData = GC.AllocateUninitializedArray <XColor>(pixelCount); target.GetData(0, targetArea, targetData, 0, pixelCount); // merge pixels for (int i = 0; i < sourceData.Length; i++) { var above = sourceData[i]; var below = targetData[i]; // shortcut transparency if (above.A < MinOpacity) { sourceData[i] = below; continue; } if (below.A < MinOpacity) { sourceData[i] = above; continue; } // merge pixels // This performs a conventional alpha blend for the pixels, which are already // premultiplied by the content pipeline. The formula is derived from // https://shawnhargreaves.com/blog/premultiplied-alpha.html. float alphaBelow = 1 - (above.A / 255f); sourceData[i] = new XColor( (int)(above.R + (below.R * alphaBelow)), // r (int)(above.G + (below.G * alphaBelow)), // g (int)(above.B + (below.B * alphaBelow)), // b Math.Max(above.A, below.A) // a ); } } // patch target texture target.SetData(0, targetArea, sourceData, 0, pixelCount); return(false); }
/// <summary>Apply the patch to a loaded asset.</summary> /// <typeparam name="T">The asset type.</typeparam> /// <param name="asset">The asset to edit.</param> public override void Edit <T>(IAssetData asset) { string errorPrefix = $"Can't apply image patch \"{this.LogName}\" to {this.TargetAsset}"; // validate if (typeof(T) != typeof(Texture2D)) { this.Monitor.Log($"{errorPrefix}: this file isn't an image file (found {typeof(T)}).", LogLevel.Warn); return; } if (!this.FromAssetExists()) { this.Monitor.Log($"{errorPrefix}: the {nameof(PatchConfig.FromFile)} file '{this.FromAsset}' doesn't exist.", LogLevel.Warn); return; } // fetch data IAssetDataForImage editor = asset.AsImage(); Texture2D source = this.ContentPack.Load <Texture2D>(this.FromAsset); if (!this.TryReadArea(this.FromArea, 0, 0, source.Width, source.Height, out Rectangle sourceArea, out string error)) { this.Monitor.Log($"{errorPrefix}: the source area is invalid: {error}.", LogLevel.Warn); return; } if (!this.TryReadArea(this.ToArea, 0, 0, sourceArea.Width, sourceArea.Height, out Rectangle targetArea, out error)) { this.Monitor.Log($"{errorPrefix}: the target area is invalid: {error}.", LogLevel.Warn); return; } // validate error conditions if (sourceArea.X < 0 || sourceArea.Y < 0 || sourceArea.Width < 0 || sourceArea.Height < 0) { this.Monitor.Log($"{errorPrefix}: source area (X:{sourceArea.X}, Y:{sourceArea.Y}, Width:{sourceArea.Width}, Height:{sourceArea.Height}) has negative values, which isn't valid.", LogLevel.Error); return; } if (targetArea.X < 0 || targetArea.Y < 0 || targetArea.Width < 0 || targetArea.Height < 0) { this.Monitor.Log($"{errorPrefix}: target area (X:{targetArea.X}, Y:{targetArea.Y}, Width:{targetArea.Width}, Height:{targetArea.Height}) has negative values, which isn't valid.", LogLevel.Error); return; } if (targetArea.Right > editor.Data.Width) { this.Monitor.Log($"{errorPrefix}: target area (X:{targetArea.X}, Y:{targetArea.Y}, Width:{targetArea.Width}, Height:{targetArea.Height}) extends past the right edge of the image (Width:{editor.Data.Width}), which isn't allowed. Patches can only extend the tilesheet downwards.", LogLevel.Error); return; } if (sourceArea.Width != targetArea.Width || sourceArea.Height != targetArea.Height) { string sourceAreaLabel = this.FromArea != null ? $"{nameof(this.FromArea)}" : "source image"; string targetAreaLabel = this.ToArea != null ? $"{nameof(this.ToArea)}" : "target image"; this.Monitor.Log($"{errorPrefix}: {sourceAreaLabel} size (Width:{sourceArea.Width}, Height:{sourceArea.Height}) doesn't match {targetAreaLabel} size (Width:{targetArea.Width}, Height:{targetArea.Height}).", LogLevel.Error); return; } // extend tilesheet if needed this.ResizedLastImage = editor.ExtendImage(editor.Data.Width, targetArea.Bottom); // apply source image editor.PatchImage(source, sourceArea, targetArea, this.PatchMode); }
public void Edit <T>(IAssetData asset) { VoidshroomSpore.setIndex(); //get an item index for voidshroom spores if one isn't already set. CaveCarrotSeed.setIndex(); CaveCarrot.setIndex(); CaveCarrot.setCropIndex(); if (asset.AssetNameEquals("Maps\\springobjects")) { IAssetDataForImage editor = asset.AsImage(); Texture2D data = editor.Data; Texture2D texture2D = new Texture2D(Game1.graphics.GraphicsDevice, data.Width, Math.Max(data.Height, 4096)); editor.ReplaceWith(texture2D); editor.PatchImage(data, new Rectangle?(), new Rectangle?(), PatchMode.Replace); try { editor.PatchImage(TextureSet.voidShroomSpore, new Rectangle?(), new Rectangle?(this.objectRect(VoidshroomSpore.getIndex())), PatchMode.Replace); editor.PatchImage(TextureSet.caveCarrotSeed, new Rectangle?(), new Rectangle?(this.objectRect(CaveCarrotSeed.getIndex())), PatchMode.Replace); } catch (Exception) { } } else if (asset.AssetNameEquals("Data\\ObjectInformation")) { IAssetDataForDictionary <int, string> editor = asset.AsDictionary <int, string>(); IDictionary <int, string> data = editor.Data; if (!data.ContainsKey(VoidshroomSpore.getIndex())) { int voidShroomSporeIndex = VoidshroomSpore.getIndex(); String voidShroomSpore = VoidshroomSpore.getObjectData(); this.log("Add voidshroom spore object data: " + voidShroomSporeIndex + ": " + voidShroomSpore); data.Add(voidShroomSporeIndex, voidShroomSpore); } if (!data.ContainsKey(CaveCarrotSeed.getIndex())) { int caveCarrotSeedIndex = CaveCarrotSeed.getIndex(); String caveCarrotObject = CaveCarrotSeed.getObjectData(); this.log("Add cave carrot seed object data: " + caveCarrotSeedIndex + ": " + caveCarrotObject); data.Add(caveCarrotSeedIndex, caveCarrotObject); } if (!data.ContainsKey(CaveCarrotFlower.getIndex())) { int caveCarrotFlowerIndex = CaveCarrotFlower.getIndex(); String caveCarrotFlowerObject = CaveCarrotFlower.getObjectData(); this.log("Add cave carrot flower 'seed' data: " + caveCarrotFlowerIndex + ": " + caveCarrotFlowerObject); data.Add(caveCarrotFlowerIndex, caveCarrotFlowerObject); } } else if (asset.AssetNameEquals("Data\\Crops")) { IAssetDataForDictionary <int, string> editor = asset.AsDictionary <int, string>(); IDictionary <int, string> data = editor.Data; int seedIndex = CaveCarrot.getIndex(); this.log("seedIndex is: " + seedIndex); if (!data.ContainsKey(seedIndex)) { String cropData = CaveCarrot.getCropData(); this.monitor.Log("Loading crop data: " + cropData); data.Add(CaveCarrot.getIndex(), cropData); } int caveCarrotFlowerIndex = CaveCarrotFlower.getIndex(); this.log("seedIndex is: " + caveCarrotFlowerIndex); if (!data.ContainsKey(caveCarrotFlowerIndex)) { String cropData = CaveCarrotFlower.getCropData(); this.monitor.Log("Loading crop data: " + cropData); data.Add(caveCarrotFlowerIndex, cropData); } } else if (asset.AssetNameEquals("TileSheets\\crops")) { IAssetDataForImage editor = asset.AsImage(); Texture2D data = editor.Data; Texture2D texture2D = new Texture2D(Game1.graphics.GraphicsDevice, data.Width, Math.Max(data.Height, 4096)); editor.ReplaceWith(texture2D); editor.PatchImage(data, new Rectangle?(), new Rectangle?(), PatchMode.Replace); try { int index = CaveCarrot.getCropIndex(); this.monitor.Log("Loading cave carrot crop texture. Crop index: " + index); editor.PatchImage(TextureSet.caveCarrotCrop, new Rectangle?(), new Rectangle?(this.cropRect(index)), PatchMode.Replace); index = CaveCarrotFlower.getCropIndex(); this.monitor.Log("Loading cave carrot flower crop texture. Crop index: " + index); editor.PatchImage(TextureSet.caveCarrotFlowerCrop, new Rectangle?(), new Rectangle?(this.cropRect(index)), PatchMode.Replace); } catch (Exception) { } } }
public void Edit <T>(IAssetData asset) { if (asset.AssetNameEquals(System.IO.Path.Combine("Data", "ObjectInformation"))) { IDictionary <int, string> data = asset.AsDictionary <int, string>().Data; if (Constants.CurrentSavePath == null) { List <int> dataKeys = new List <int>(data.Keys); dataKeys.Sort(); largestItemID = dataKeys[dataKeys.Count - 1]; } else { string smallEggData = "Egg/{{sell price}}/10/Basic -5/Egg/A{{species}} egg."; string largeEggData = "Large Egg/{{sell price}}/15/Basic -5/Large Egg/It's an uncommonly large {{species}} egg!"; string eggData; foreach (int id in modData.customEggsAndHatchedAnimals.Keys) { string speciesName = modData.customEggsAndHatchedAnimals[id][modDataBreedIndex].ToLower(); if (IsDeluxeEgg(id)) { eggData = largeEggData; if (modData.customEggsAndHatchedAnimals[id][modDataEggAspectIndex] != "true") { eggData = eggData.Replace("large", modData.customEggsAndHatchedAnimals[id][modDataEggAspectIndex]); } int multiplier = DeluxeProductMultiplier(id); if (multiplier > 1) { eggData = eggData.Replace("Large Egg", "Egg x" + multiplier); } } else { eggData = smallEggData; if ("aeiouAEIOU".IndexOf(speciesName[0]) >= 0) { speciesName = "n " + speciesName; } else { speciesName = " " + speciesName; } } eggData = eggData.Replace("{{species}}", speciesName).Replace("{{sell price}}", modData.customEggsAndHatchedAnimals[id][modDataPriceIndex]); if (!data.ContainsKey(id)) { data.Add(id, eggData); } } } } else if (chickenContentHandled && asset.AssetNameEquals(System.IO.Path.Combine("Data", "FarmAnimals"))) { EditFarmAnimalsData(asset); } else if (eggContentHandled && asset.AssetNameEquals(System.IO.Path.Combine("Maps", "springobjects"))) { foreach (int itemID in customEggTextures.Keys) { Vector2 spriteSheetCoords = GetSpritesheetCoordsFromItemID(itemID); IAssetDataForImage editor = asset.AsImage(); Texture2D eggTexture = smapiHelper.Content.Load <Texture2D>(customEggTextures[itemID], ContentSource.GameContent); Rectangle fromArea = new Rectangle(0, 0, 16, 16); Rectangle targetArea = new Rectangle((int)spriteSheetCoords.X, (int)spriteSheetCoords.Y, 16, 16); // extend tilesheet if needed if (targetArea.Bottom > editor.Data.Height) { Texture2D original = editor.Data; Texture2D texture = new Texture2D(Game1.graphics.GraphicsDevice, original.Width, targetArea.Bottom); editor.ReplaceWith(texture); editor.PatchImage(original); } editor.PatchImage(eggTexture, fromArea, targetArea, PatchMode.Replace); } } }