diff --git a/src/Controllers/AmogusController.cs b/src/Controllers/AmogusController.cs new file mode 100644 index 0000000..a1b80d8 --- /dev/null +++ b/src/Controllers/AmogusController.cs @@ -0,0 +1,44 @@ +using Exsersewo.Common.Utilities; +using ImageMagick; +using Kynareth.Helpers; +using Microsoft.AspNetCore.Mvc; + +namespace Kynareth.Controllers; + +[Route("amogus")] +public class AmogusController : BaseController +{ + public AmogusController(IConfiguration configuration, ILoggerFactory loggerFactory, IWebHostEnvironment appEnvironment) : base(configuration, loggerFactory, appEnvironment) + { + } + + [HttpGet("eject")] + public async Task Eject([FromQuery] string name, [FromQuery] AmogusRole role = AmogusRole.Unknown, [FromQuery] AmogusColour colour = AmogusColour.Red, [FromQuery] AmogusSkin skin = AmogusSkin.None) + { + if (string.IsNullOrWhiteSpace(name)) return StatusCode(StatusCodes.Status400BadRequest, EventResult.FromFailure(nameof(name) + " needs to be provided.")); + + using var result = AmogusHelper.GenerateEjectionGif(name, colour, role, skin); + + if (result is MagickImageCollection data) + { + var memStream = new MemoryStream(); + data.Write(memStream, data.Count <= 1 ? MagickFormat.Png : MagickFormat.Gif); + + return HttpContext.SendStream(memStream, data.Count <= 1 ? "image/png" : "image/gif"); + } + + return StatusCode(StatusCodes.Status500InternalServerError); + } + + [HttpGet("roles")] + public async Task GetAmongUsRoles() + => Ok(EventResult.FromSuccess(Enum.GetNames().Except("None"))); + + [HttpGet("colours")] + public async Task GetAmongUsColours() + => Ok(EventResult.FromSuccess(Enum.GetNames().Except("None"))); + + [HttpGet("skins")] + public async Task GetAmongUsSkins() + => Ok(EventResult.FromSuccess(Enum.GetNames().Except("None"))); +} \ No newline at end of file diff --git a/src/Extensions.cs b/src/Extensions.cs new file mode 100644 index 0000000..3d32fb7 --- /dev/null +++ b/src/Extensions.cs @@ -0,0 +1,22 @@ +namespace Kynareth; + +public static class Extensions +{ + public static T GetRandomEnumValue() where T : Enum + => (T) Enum.GetValues(typeof(T)).OfType().OrderBy(_ => Guid.NewGuid()).FirstOrDefault(); + + public static IList Range(int start, int stop, int step) + { + var list = new List(); + + for (int i = start; i < stop; i += step) { + list.Add(i); + } + + return list; + } + + public static T[] Except(this T[] array, T specificValue) where T : IComparable { + return array.Where(val => val.CompareTo(specificValue) != 0).ToArray(); + } +} \ No newline at end of file diff --git a/src/Helpers/AmogusHelper.cs b/src/Helpers/AmogusHelper.cs new file mode 100644 index 0000000..d551e5e --- /dev/null +++ b/src/Helpers/AmogusHelper.cs @@ -0,0 +1,579 @@ +using System.Drawing; +using System.Numerics; +using System.Text; +using ImageMagick; +using Kynareth.Managers; +using Kynareth.Models; + +namespace Kynareth.Helpers; + +public enum AmogusRole +{ + Imposter, + Crewmate, + Unknown +} + +public enum AmogusColour +{ + Red, + Blue, + Green, + Pink, + Orange, + Yellow, + Black, + White, + Purple, + Brown, + Cyan, + Lime, + Maroon, + Rose, + Banana, + Gray, + Tan, + Coral, +} + +public enum AmogusSkin +{ + Archaeologist, + Astronaut, + Captain, + Hazmat, + Mechanic, + Military, + Miner, + Police, + SecurityGuard, + Scientist, + BlackSuit, + WhiteSuit, + Tarmac, + Wall, + Winter, + None, +} + +public static class AmogusHelper +{ + #region CoreVariables + public class ColourHelper + { + private static readonly Dictionary _RedReplaceColors = new() + { + { AmogusColour.Red, new(215, 30, 34, 255) }, + { AmogusColour.Blue, new(29, 60, 233, 255) }, + { AmogusColour.Green, new(27, 145, 62, 255) }, + { AmogusColour.Pink, new(255, 99, 212, 255) }, + { AmogusColour.Orange, new(255, 141, 28, 255) }, + { AmogusColour.Yellow, new(255, 255, 103, 255) }, + { AmogusColour.Black, new(74, 86, 94, 255) }, + { AmogusColour.White, new(233, 247, 255, 255) }, + { AmogusColour.Purple, new(120, 61, 210, 255) }, + { AmogusColour.Brown, new(128, 88, 45, 255) }, + { AmogusColour.Cyan, new(68, 255, 247, 255) }, + { AmogusColour.Lime, new(91, 255, 75, 255) }, + { AmogusColour.Maroon, new(108, 43, 61, 255) }, + { AmogusColour.Rose, new(255, 214, 236, 255) }, + { AmogusColour.Banana, new(255, 255, 190, 255) }, + { AmogusColour.Gray, new(131, 151, 167, 255) }, + { AmogusColour.Tan, new(159, 153, 137, 255) }, + { AmogusColour.Coral, new(236, 117, 120, 255) }, + }; + + private static readonly Dictionary _BlueReplaceColors = new() + { + { AmogusColour.Red, new(122, 8, 56, 255) }, + { AmogusColour.Blue, new(9, 21, 142, 255) }, + { AmogusColour.Green, new(10, 77, 46, 255) }, + { AmogusColour.Pink, new(172, 43, 174, 255) }, + { AmogusColour.Orange, new(180, 62, 21, 255) }, + { AmogusColour.Yellow, new(195, 136, 34, 255) }, + { AmogusColour.Black, new(30, 31, 38, 255) }, + { AmogusColour.White, new(132, 149, 192, 255) }, + { AmogusColour.Purple, new(59, 23, 124, 255) }, + { AmogusColour.Brown, new(94, 38, 21, 255) }, + { AmogusColour.Cyan, new(36, 169, 191, 255) }, + { AmogusColour.Lime, new(21, 168, 66, 255) }, + { AmogusColour.Maroon, new(65, 15, 26, 255) }, + { AmogusColour.Rose, new(222, 146, 179, 255) }, + { AmogusColour.Banana, new(210, 188, 137, 255) }, + { AmogusColour.Gray, new(70, 86, 100, 255) }, + { AmogusColour.Tan, new(81, 65, 62, 255) }, + { AmogusColour.Coral, new(180, 67, 98, 255) }, + }; + + private static readonly MagickColor _green = new MagickColor(149, 202, 220, 255); + private readonly MagickColor _red; + private readonly MagickColor _blue; + + public MagickColor Red => _red; + public MagickColor Green => _green; + public MagickColor Blue => _blue; + + public ColourHelper(AmogusColour colour = AmogusColour.Blue) + { + _red = _RedReplaceColors[colour]; + _blue = _BlueReplaceColors[colour]; + } + } + + public static readonly Dictionary IdleSkins = new() + { + { AmogusSkin.Archaeologist, "archae-idle.png" }, + { AmogusSkin.Astronaut, "astro-idle.png" }, + { AmogusSkin.Captain, "captain-idle.png" }, + { AmogusSkin.Hazmat, "hazmat-idle.png" }, + { AmogusSkin.Mechanic, "mech-idle.png" }, + { AmogusSkin.Military, "military-idle.png" }, + { AmogusSkin.Miner, "miner-idle.png" }, + { AmogusSkin.Police, "pol-idle.png" }, + { AmogusSkin.SecurityGuard, "secguard-idle.png" }, + { AmogusSkin.Scientist, "sci-idle.png" }, + { AmogusSkin.BlackSuit, "suitBlack-idle.png" }, + { AmogusSkin.WhiteSuit, "suitWhite-idle.png" }, + { AmogusSkin.Tarmac, "tarmac-idle.png" }, + { AmogusSkin.Wall, "wall-idle.png" }, + { AmogusSkin.Winter, "winter-idle.png" }, + }; + + public static readonly Dictionary EjectSkins = new() + { + { AmogusSkin.Archaeologist, "archae-eject.png" }, + { AmogusSkin.Astronaut, "astro-eject.png" }, + { AmogusSkin.Captain, "captain-eject.png" }, + { AmogusSkin.Hazmat, "hazmat-eject.png" }, + { AmogusSkin.Mechanic, "mech-eject.png" }, + { AmogusSkin.Military, "military-eject.png" }, + { AmogusSkin.Miner, "miner-eject.png" }, + { AmogusSkin.Police, "police-eject.png" }, + { AmogusSkin.SecurityGuard, "secguard-eject.png" }, + { AmogusSkin.Scientist, "sci-eject.png" }, + { AmogusSkin.BlackSuit, "suitBlack-eject.png" }, + { AmogusSkin.WhiteSuit, "suitWhite-eject.png" }, + { AmogusSkin.Tarmac, "tarmac-eject.png" }, + { AmogusSkin.Wall, "wall-eject.png" }, + { AmogusSkin.Winter, "winter-eject.png" }, + }; + + public static readonly Dictionary IdleOffset = new() + { + { AmogusSkin.Archaeologist, new(13, 41) }, + { AmogusSkin.Astronaut, new(13, 46) }, + { AmogusSkin.Captain, new(14, 45) }, + { AmogusSkin.Hazmat, new(12, 34) }, + { AmogusSkin.Mechanic, new(13, 46) }, + { AmogusSkin.Military, new(11, 45) }, + { AmogusSkin.Miner, new(13, 40) }, + { AmogusSkin.Police, new(10, 45) }, + { AmogusSkin.SecurityGuard, new(13, 42) }, + { AmogusSkin.Scientist, new(14, 43) }, + { AmogusSkin.BlackSuit, new(14, 44) }, + { AmogusSkin.WhiteSuit, new(14, 44) }, + { AmogusSkin.Tarmac, new(14, 40) }, + { AmogusSkin.Wall, new(10, 44) }, + { AmogusSkin.Winter, new(9, 35) }, + }; + + public static readonly Dictionary EjectOffset = new() + { + { AmogusSkin.Archaeologist, new(12, 35) }, + { AmogusSkin.Astronaut, new(12, 35) }, + { AmogusSkin.Captain, new(12, 35) }, + { AmogusSkin.Hazmat, new(13, 37) }, + { AmogusSkin.Mechanic, new(13, 35) }, + { AmogusSkin.Military, new(12, 35) }, + { AmogusSkin.Miner, new(12, 36) }, + { AmogusSkin.Police, new(12, 35) }, + { AmogusSkin.SecurityGuard, new(13, 35) }, + { AmogusSkin.Scientist, new(-10, 35) }, + { AmogusSkin.BlackSuit, new(12, 35) }, + { AmogusSkin.WhiteSuit, new(11, 35) }, + { AmogusSkin.Tarmac, new(14, 37) }, + { AmogusSkin.Wall, new(12, 35) }, + { AmogusSkin.Winter, new(5, 30) }, + }; + + public static string IdleRoot, EjectRoot, CommonRoot; + + public const string Arial = "Arial"; + #endregion CoreVariables + + public static void Configure(IWebHostEnvironment appEnvironment) + { + string amogusBase = Path.Combine(appEnvironment.WebRootPath, "amogus"); + + IdleRoot = Path.Combine(amogusBase, "idle"); + EjectRoot = Path.Combine(amogusBase, "eject"); + CommonRoot = Path.Combine(amogusBase, "common"); + } + + /// + /// Generates a base image for an AmongUs Asset that needs color replacement + /// The default image is the standing crewmate (idle) and the default colour is blue + /// + /// The image to load up + /// The colour to make the base image, must be one + /// A generated from + static MagickImage GenerateBase(string baseImageName = "idle.png", AmogusColour colour = AmogusColour.Blue) + { + string baseIdle = Path.Combine(CommonRoot, baseImageName); + MagickImage body = new MagickImage(baseIdle); + + body = ColourReplace(body, colour); + body.BackgroundColor = MagickColors.Transparent; + + return body; + } + + /// + /// Replaces the Colour of an AmogieCrewMate with the target colour + /// + /// Image to Colour Replace + /// Colour to replace with + /// Colour Replaced Amogie Crewmate + static MagickImage ColourReplace(MagickImage image, AmogusColour colour = AmogusColour.Blue) + { + var colours = new ColourHelper(colour); + + var pixels = image.GetPixels(); + + for (int y = 0; y < image.Height; y++) + { + for (int x = 0; x < image.Width; x++) + { + var pixel = pixels.GetPixel(x, y); + var pixelColor = pixel.ToColor(); + + if (pixelColor is null) continue; + + float pixelSum = pixelColor.R + pixelColor.G + pixelColor.B; + + var areEqual = pixelColor.R == pixelColor.G && pixelColor.G == pixelColor.B; + + if (pixelColor.A == 0 || areEqual) continue; + + var pixBr = pixelColor.ToByteArray(); + + int SumWithinRange(byte lower, byte upper) + { + int count = 0; + + if (pixelColor.R >= lower && pixelColor.R <= upper) count++; + if (pixelColor.G >= lower && pixelColor.G <= upper) count++; + if (pixelColor.B >= lower && pixelColor.B <= upper) count++; + + return count; + } + + if (SumWithinRange(45, 65) < 3) + { + byte[] + newR = Enumerable.Repeat((byte)(pixelColor.R / 255), 3).ToArray(), + newG = new byte[3], + newB = Enumerable.Repeat((byte)(pixelColor.B / 255), 3).ToArray(); + + var rBArr = colours.Red.ToByteArray(); + var gBArr = colours.Green.ToByteArray(); + var bBArr = colours.Blue.ToByteArray(); + + int m = 0; + foreach (var mask in gBArr) + { + if (m == 3) break; + newG[m] = (byte)(mask * pixelColor.G / 255); + m++; + } + + if (pixelColor.G == 0 || pixelSum is >= 508 and <= 520) + { + m = 0; + foreach (var mask in rBArr) + { + if (m == 3) break; + newR[m] = (byte)(mask * pixelColor.R / 255); + m++; + } + + + m = 0; + foreach (var mask in bBArr) + { + if (m == 3) break; + newB[m] = (byte)(mask * pixelColor.B / 255); + m++; + } + } + else if (pixelColor.R == pixelColor.B && pixelSum > 512) + { + m = 0; + foreach (var mask in newG) + { + if (m == 3) break; + byte val = (byte)(((pixelColor.R / 255) * (255 - mask)) / 2); + newR[m] = val; + newB[m] = val; + } + } + + byte minR = (byte)Math.Min(newR[0] + newG[0] + newB[0], 255); + byte minG = (byte)Math.Min(newR[1] + newG[1] + newB[1], 255); + byte minB = (byte)Math.Min(newR[2] + newG[2] + newB[2], 255); + + byte[] newColors = { minR, minG, minB, pixelColor.A }; + + pixels.SetPixel(x, y, newColors); + } + } + } + + image.ImportPixels(pixels.ToArray(), new PixelImportSettings(0, 0, image.Width, image.Height, StorageType.Quantum, PixelMapping.RGBA)); + + return image; + } + + /// + /// Applies the Eject variant of the Skin + /// + /// Base Crewmate Image + /// Skin to Apply + /// Skin offset from + /// Origin of + /// Crewmate with Skin Applied + static MagickImage ApplyLayer(MagickImage baseImage, MagickImage layerImage, Point layerOrigin, Point baseOrigin) + { + int left = layerOrigin.X + baseOrigin.X; + int top = layerOrigin.Y + baseOrigin.Y; + int right = layerOrigin.X + layerImage.Width - baseImage.Width; + int bottom = layerOrigin.Y + layerImage.Height - baseImage.Height; + + left = left < 0 ? Math.Abs(left) : 0; + top = top < 0 ? Math.Abs(top) : 0; + + MagickImage newImage = new MagickImage(MagickColors.Transparent, left + right + baseImage.Width, top + bottom + baseImage.Height); + newImage.BackgroundColor = MagickColors.Transparent; + + baseOrigin = new Point(left, top); + Point newLayerOrigin = new Point(baseOrigin.X + layerOrigin.X, baseOrigin.Y + layerOrigin.Y); + + newImage.Composite(baseImage, baseOrigin.X, baseOrigin.Y, CompositeOperator.Over); + newImage.Composite(layerImage, newLayerOrigin.X, newLayerOrigin.Y, CompositeOperator.Over); + + return newImage; + } + + /// + /// Generate a crewmate with the given settings + /// + /// Colour of the crewmate + /// Skin that the crewmate is wearing + /// If the crewmate has been ejected or not + /// Generated crewmate with the given settings + static MagickImage GenerateCrewmate(AmogusColour colour, AmogusSkin skin, bool ejected = true) + { + MagickImage baseImage = GenerateBase(ejected ? "ejected.png" : "idle.png", colour); + MagickImage skinImage = null; + + if (skin != AmogusSkin.None) + { + skinImage = new MagickImage( + Path.Combine( + ejected ? EjectRoot : IdleRoot, + ejected ? EjectSkins[skin] : IdleSkins[skin] + ) + ); + } + + var outImage = skinImage != null ? + ApplyLayer( + baseImage, + skinImage, + ejected ? EjectOffset[skin] : IdleOffset[skin], new Point(0, 0) + ) : baseImage; + + outImage.Trim(); + + return outImage; + } + + /// + /// Generate Amongus Stars + /// + /// Width of the stars + /// Generated image of stars + static MagickImage GenerateStars(int width = 2000) + { + MagickImage baseImage = new MagickImage("xc:transparent", width, 588); + + using MagickImage stars = new MagickImage(Path.Combine(CommonRoot, "Stars.png")); + stars.Distort(DistortMethod.ScaleRotateTranslate, 90); + using MagickImage stars2 = new MagickImage(stars); + stars2.Distort(DistortMethod.ScaleRotateTranslate, 180); + + foreach (var x in Extensions.Range(-800, width, 320)) + { + baseImage.Composite(stars2, Gravity.Northwest, x, (int)(x / 60f) - 200, CompositeOperator.Over); + baseImage.Composite(stars, Gravity.Northwest, x, 100 + (int)(x / 120f), CompositeOperator.Over); + baseImage.Composite(stars2, Gravity.Northwest, x, 300 - (int)(x / 60f), CompositeOperator.Over); + baseImage.Composite(stars, Gravity.Northwest, x, 500 + (int)(x / 120f), CompositeOperator.Over); + baseImage.Composite(stars, Gravity.Northwest, x + 160 + (int)(x / 80f), (int)(x / 100f) - 100, CompositeOperator.Over); + baseImage.Composite(stars2, Gravity.Northwest, x + 160 - (int)(x / 80f), 100 + (int)(x / 120f), CompositeOperator.Over); + baseImage.Composite(stars, Gravity.Northwest, x + 160 + (int)(x / 80f), 300 - (int)(x / 100f), CompositeOperator.Over); + baseImage.Composite(stars2, Gravity.Northwest, x + 160 - (int)(x / 80f), 500 - (int)(x / 100f), CompositeOperator.Over); + } + + return baseImage; + } + + static string GenerateEjectionMessage(string name = "I", AmogusRole role = AmogusRole.Unknown) + { + StringBuilder message = new StringBuilder(name); + message.Append(" was"); + + if (role == AmogusRole.Unknown) + { + message.Append(" ejected."); + return message.ToString(); + } + + if (role == AmogusRole.Crewmate) + { + message.Append(" not"); + } + + message.Append(" An Impostor."); + + return message.ToString(); + } + + /// + /// Generates the Ejection Gif with all modifications applied + /// + /// + /// + /// + /// + /// A containing the Amogus Ejected Gif + public static MagickImageCollection GenerateEjectionGif(string name, AmogusColour? colour = null, AmogusRole? role = null, AmogusSkin? skin = null, bool addStars = true) + { + if (role is null) role = Extensions.GetRandomEnumValue(); + + return GenerateCustomEjectionGif(GenerateEjectionMessage(name, role.Value), colour, role, skin, addStars); + } + + public static MagickImageCollection GenerateCustomEjectionGif(string text, AmogusColour? colour = null, AmogusRole? role = null, AmogusSkin? skin = null, bool addStars = true) + { + if (colour is null) colour = Extensions.GetRandomEnumValue(); + if (role is null) role = Extensions.GetRandomEnumValue(); + if (skin is null) skin = Extensions.GetRandomEnumValue(); + + var crewmate = GenerateCrewmate(colour.Value, skin.Value); + crewmate = MakeSquare(crewmate); + var textImg = new MagickImage(MagickColors.Transparent, 4000, 64); + textImg.WriteText(text, 0, 0, MagickColors.White, Encoding.Default, 24, Arial, 0, MagickColors.Transparent); + textImg.Crop(textImg.BoundingBox); + textImg.RePage(); + + (int X, int Y) center = new(crewmate.Width / 2, crewmate.Height / 2); + + using var background = new MagickImage(MagickColors.Black, Math.Max(textImg.Width + 100, 600), 300); + + MagickImage stars = null, stars1 = null, stars2 = null, stars3 = null; + + if (addStars) + { + stars = GenerateStars(background.Width * 3); + + stars1 = stars.Clone() as MagickImage; + stars1.BackgroundColor = MagickColors.Transparent; + stars1.Resize((int)(stars.Width * 3.5f), (int)(stars.Height * 3.5f)); + + stars2 = stars1.Clone() as MagickImage; + stars2.BackgroundColor = MagickColors.Transparent; + + stars3 = stars1.Clone() as MagickImage; + stars3.BackgroundColor = MagickColors.Transparent; + } + + var range = Extensions.Range(-120, background.Width * 3, 12); + + MagickImageCollection gifFrames = new MagickImageCollection(); + + foreach (var bodyX in range) + { + var image = background.Clone() as MagickImage; + + if (addStars) + { + image.Composite(stars1, -background.Width + (int)(bodyX / 24f) - 100, -200, CompositeOperator.Over); + image.Composite(stars2, -background.Width + (int)(bodyX / 12f) - 200, -800, CompositeOperator.Over); + image.Composite(stars3, -background.Width + (int)(bodyX / 6f) - 100, -425, CompositeOperator.Over); + } + + using var rotatedBody = crewmate.Clone() as MagickImage; + rotatedBody.BackgroundColor = MagickColors.Transparent; + rotatedBody.Distort(DistortMethod.ScaleRotateTranslate, -(2 * bodyX / 3)); + rotatedBody.RePage(); + + var rotateX = bodyX - center.X; + var rotateY = background.Height / 2 - center.Y; + + image.Composite(rotatedBody, Gravity.Northwest, rotateX, rotateY, CompositeOperator.Atop); + + using var txt = textImg.Clone() as MagickImage; + txt.BackgroundColor = MagickColors.Transparent; + + if (bodyX > background.Width / 2 && bodyX <= 3 * background.Width / 2) + { + var midX = background.Width / 2; + var textY = background.Height / 2 - txt.Height / 2; + var textX = Convert.ToInt32(midX - ((float)bodyX - midX) / background.Width * txt.Width / 2f); + var textWidth = (midX - textX) * 2; + + txt.Crop(new MagickGeometry(0, 0, textWidth, txt.Height)); + + image.Composite(txt, Gravity.Northwest, textX, textY, CompositeOperator.Over); + } + else if (bodyX > (int)(background.Width * 1.5f) && bodyX <= (int)(background.Width * 2.5f)) + { + image.Composite(txt, (int)(background.Width / 2f - txt.Width / 2f), (int)(background.Height / 2f - txt.Height / 2f), CompositeOperator.Atop); + } + + image.AnimationIterations = 0; + image.AnimationDelay = 4; + + gifFrames.Add(image); + } + + if (addStars) + { + stars.Dispose(); + stars1.Dispose(); + stars2.Dispose(); + stars3.Dispose(); + } + + crewmate.Dispose(); + + return gifFrames; + } + + #region Utilities + static MagickImage MakeSquare(MagickImage image) + { + int size = Math.Max(image.Width, image.Height); + + MagickImage newImage = new MagickImage("xc:transparent", size, size); + newImage.Format = MagickFormat.Png; + + int x = (size - image.Width) / 2; + int y = (size - image.Height) / 2; + + newImage.Composite(image, Gravity.Center, x, y, CompositeOperator.Over); + + return newImage; + } + #endregion Utilities +} \ No newline at end of file diff --git a/src/Helpers/StreamResult.cs b/src/Helpers/StreamResult.cs index ec127d6..229111f 100644 --- a/src/Helpers/StreamResult.cs +++ b/src/Helpers/StreamResult.cs @@ -32,13 +32,14 @@ public class StreamResult : ActionResult, IStatusCodeActionResult, IActionResult ? ContentType : "image/png"; + Value.Position = 0; + await Value.CopyToAsync(response.Body); await Value.FlushAsync(); await Value.DisposeAsync(); - context.HttpContext.Response.RegisterForDisposeAsync(Value); GC.Collect(); diff --git a/src/Managers/ImageManager.cs b/src/Managers/ImageManager.cs index 714bd40..afa9eeb 100644 --- a/src/Managers/ImageManager.cs +++ b/src/Managers/ImageManager.cs @@ -1,3 +1,4 @@ +using System.Numerics; using System.Text; using ImageMagick; using Kynareth.Models; @@ -15,8 +16,28 @@ public static partial class ImageManager private static ObjectResult GetResult(int statusCode, object data) => new(data) { StatusCode = statusCode }; + + public static MagickImage WriteText(this MagickImage image, string text, PositionRect rect, MagickColor fontColor, Encoding encoding, string font = "Impact", int strokeWidth = 2, MagickColor? strokeColor = null) + { + using var label = new MagickImage($"label:{text}", new MagickReadSettings() + { + Width = rect.Width, + Height = rect.Height, + BackgroundColor = MagickColors.Transparent, + FillColor = fontColor, + TextGravity = Gravity.Center, + Font = font, + TextEncoding = encoding, + StrokeColor = new MagickColor(strokeColor ?? MagickColors.Black), + StrokeWidth = strokeWidth + }); - public static MagickImage WriteText(this MagickImage image, string text, PositionRect rect, MagickColor fontColor, Encoding encoding, double fontSize, string font = "Impact") + image.Composite(label, rect.X, rect.Y, CompositeOperator.Over); + + return image; + } + + public static MagickImage WriteText(this MagickImage image, string text, PositionRect rect, MagickColor fontColor, Encoding encoding, double fontSize, string font = "Impact", int strokeWidth = 2, MagickColor? strokeColor = null) { using var label = new MagickImage($"caption:{text}", new MagickReadSettings() { @@ -28,8 +49,8 @@ public static partial class ImageManager FontPointsize = fontSize, Font = font, TextEncoding = encoding, - StrokeColor = MagickColors.Black, - StrokeWidth = 2 + StrokeColor = new MagickColor(strokeColor ?? MagickColors.Black), + StrokeWidth = strokeWidth }); image.Composite(label, rect.X, rect.Y, CompositeOperator.Over); @@ -37,22 +58,125 @@ public static partial class ImageManager return image; } - public static MagickImage WriteText(this MagickImage image, string text, PositionRect rect, MagickColor fontColor, Encoding encoding, string font = "Impact") + public static MagickImage WriteText(this MagickImage image, string text, Vector2 position, MagickColor fontColor, Encoding encoding, string font = "Impact", int strokeWidth = 2, MagickColor? strokeColor = null) { - using var label = new MagickImage($"label:{text}", new MagickReadSettings() + using var label = new MagickImage($"caption:{text}", new MagickReadSettings() { - Width = rect.Width, - Height = rect.Height, + Width = image.Width, + Height = image.Height, BackgroundColor = MagickColors.Transparent, FillColor = fontColor, TextGravity = Gravity.Center, Font = font, TextEncoding = encoding, - StrokeColor = MagickColors.Black, - StrokeWidth = 2 + StrokeColor = new MagickColor(strokeColor ?? MagickColors.Black), + StrokeWidth = strokeWidth }); - image.Composite(label, rect.X, rect.Y, CompositeOperator.Over); + image.Composite(label, (int)position.X, (int)position.Y, CompositeOperator.Over); + + return image; + } + + public static MagickImage WriteText(this MagickImage image, string text, Vector2 position, MagickColor fontColor, Encoding encoding, double fontSize, string font = "Impact", int strokeWidth = 2, MagickColor? strokeColor = null) + { + using var label = new MagickImage($"caption:{text}", new MagickReadSettings() + { + Width = image.Width, + Height = image.Height, + BackgroundColor = MagickColors.Transparent, + FillColor = fontColor, + TextGravity = Gravity.Center, + FontPointsize = fontSize, + Font = font, + TextEncoding = encoding, + StrokeColor = new MagickColor(strokeColor ?? MagickColors.Black), + StrokeWidth = strokeWidth + }); + + image.Composite(label, (int)position.X, (int)position.Y, CompositeOperator.Over); + + return image; + } + + public static MagickImage WriteText(this MagickImage image, string text, PointD position, MagickColor fontColor, Encoding encoding, string font = "Impact", int strokeWidth = 2, MagickColor? strokeColor = null) + { + using var label = new MagickImage($"caption:{text}", new MagickReadSettings() + { + Width = image.Width, + Height = image.Height, + BackgroundColor = MagickColors.Transparent, + FillColor = fontColor, + TextGravity = Gravity.Center, + Font = font, + TextEncoding = encoding, + StrokeColor = new MagickColor(strokeColor ?? MagickColors.Black), + StrokeWidth = strokeWidth + }); + + image.Composite(label, (int)position.X, (int)position.Y, CompositeOperator.Over); + + return image; + } + + public static MagickImage WriteText(this MagickImage image, string text, PointD position, MagickColor fontColor, Encoding encoding, double fontSize, string font = "Impact", int strokeWidth = 2, MagickColor? strokeColor = null) + { + using var label = new MagickImage($"caption:{text}", new MagickReadSettings() + { + Width = image.Width, + Height = image.Height, + BackgroundColor = MagickColors.Transparent, + FillColor = fontColor, + TextGravity = Gravity.Center, + FontPointsize = fontSize, + Font = font, + TextEncoding = encoding, + StrokeColor = new MagickColor(strokeColor ?? MagickColors.Black), + StrokeWidth = strokeWidth + }); + + image.Composite(label, (int)position.X, (int)position.Y, CompositeOperator.Over); + + return image; + } + + public static MagickImage WriteText(this MagickImage image, string text, int x, int y, MagickColor fontColor, Encoding encoding, string font = "Impact", int strokeWidth = 2, MagickColor? strokeColor = null) + { + using var label = new MagickImage($"caption:{text}", new MagickReadSettings() + { + Width = image.Width, + Height = image.Height, + BackgroundColor = MagickColors.Transparent, + FillColor = fontColor, + TextGravity = Gravity.Center, + Font = font, + TextEncoding = encoding, + StrokeColor = new MagickColor(strokeColor ?? MagickColors.Black), + StrokeWidth = strokeWidth + }); + + image.Composite(label, x, y, CompositeOperator.Over); + + return image; + } + + public static MagickImage WriteText(this MagickImage image, string text, int x, int y, MagickColor fontColor, Encoding encoding, double fontSize, string font = "Impact", int strokeWidth = 2, MagickColor? strokeColor = null) + { + using var label = new MagickImage($"caption:{text}", new MagickReadSettings() + { + Width = image.Width, + Height = image.Height, + BackgroundColor = MagickColors.Transparent, + FillColor = fontColor, + TextGravity = Gravity.Center, + FontPointsize = fontSize, + Font = font, + TextEncoding = encoding, + StrokeColor = new MagickColor(strokeColor ?? MagickColors.Black), + StrokeWidth = strokeWidth + }); + + image.Composite(label, x, y, CompositeOperator.Over); return image; } diff --git a/src/Program.cs b/src/Program.cs index e9cd10d..2e16ad0 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -1,4 +1,5 @@ using Exsersewo.Common.Utilities; +using Kynareth.Helpers; using Kynareth.Managers; using Kynareth.Models; using Microsoft.EntityFrameworkCore; @@ -32,6 +33,7 @@ builder.Services.AddDbContext(options => var app = builder.Build(); ImageManager.Configure(app.Configuration, app.Environment); +AmogusHelper.Configure(app.Environment); // Configure the HTTP request pipeline. //if (app.Environment.IsDevelopment()) diff --git a/src/Properties/launchSettings.json b/src/Properties/launchSettings.json index b91d186..513e4e4 100644 --- a/src/Properties/launchSettings.json +++ b/src/Properties/launchSettings.json @@ -14,7 +14,7 @@ "dotnetRunMessages": true, "launchBrowser": true, "launchUrl": "swagger", - "applicationUrl": "http://localhost:5054", + "applicationUrl": "http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } @@ -24,7 +24,7 @@ "dotnetRunMessages": true, "launchBrowser": true, "launchUrl": "swagger", - "applicationUrl": "https://localhost:7072;http://localhost:5054", + "applicationUrl": "https://localhost:7072;http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/src/appsettings.json b/src/appsettings.json index 8f46ef1..4d79871 100644 --- a/src/appsettings.json +++ b/src/appsettings.json @@ -1705,6 +1705,24 @@ "h": 75 } ] + }, + { + "name": "fortune", + "image": "fortuneteller.jpg", + "textpositions": [ + { + "x": 130, + "y": 413, + "w": 101, + "h": 67 + }, + { + "x": 505, + "y": 455, + "w": 127, + "h": 66 + } + ] } ] } diff --git a/src/wwwroot/amogus/common/Dead0001.png b/src/wwwroot/amogus/common/Dead0001.png new file mode 100644 index 0000000..e71762c Binary files /dev/null and b/src/wwwroot/amogus/common/Dead0001.png differ diff --git a/src/wwwroot/amogus/common/Dead0040.png b/src/wwwroot/amogus/common/Dead0040.png new file mode 100644 index 0000000..f53a4d1 Binary files /dev/null and b/src/wwwroot/amogus/common/Dead0040.png differ diff --git a/src/wwwroot/amogus/common/SpacemanLarge.png b/src/wwwroot/amogus/common/SpacemanLarge.png new file mode 100644 index 0000000..66d3f40 Binary files /dev/null and b/src/wwwroot/amogus/common/SpacemanLarge.png differ diff --git a/src/wwwroot/amogus/common/Stars.png b/src/wwwroot/amogus/common/Stars.png new file mode 100644 index 0000000..ae3faf0 Binary files /dev/null and b/src/wwwroot/amogus/common/Stars.png differ diff --git a/src/wwwroot/amogus/common/ejected.png b/src/wwwroot/amogus/common/ejected.png new file mode 100644 index 0000000..2e9c417 Binary files /dev/null and b/src/wwwroot/amogus/common/ejected.png differ diff --git a/src/wwwroot/amogus/common/ghostbob0001.png b/src/wwwroot/amogus/common/ghostbob0001.png new file mode 100644 index 0000000..d28fc68 Binary files /dev/null and b/src/wwwroot/amogus/common/ghostbob0001.png differ diff --git a/src/wwwroot/amogus/common/idle.png b/src/wwwroot/amogus/common/idle.png new file mode 100644 index 0000000..e0e81f5 Binary files /dev/null and b/src/wwwroot/amogus/common/idle.png differ diff --git a/src/wwwroot/amogus/common/noshadow.png b/src/wwwroot/amogus/common/noshadow.png new file mode 100644 index 0000000..712042e Binary files /dev/null and b/src/wwwroot/amogus/common/noshadow.png differ diff --git a/src/wwwroot/amogus/eject/archae-eject.png b/src/wwwroot/amogus/eject/archae-eject.png new file mode 100644 index 0000000..46c97cc Binary files /dev/null and b/src/wwwroot/amogus/eject/archae-eject.png differ diff --git a/src/wwwroot/amogus/eject/astro-eject.png b/src/wwwroot/amogus/eject/astro-eject.png new file mode 100644 index 0000000..d46778b Binary files /dev/null and b/src/wwwroot/amogus/eject/astro-eject.png differ diff --git a/src/wwwroot/amogus/eject/captain-eject.png b/src/wwwroot/amogus/eject/captain-eject.png new file mode 100644 index 0000000..fa459a0 Binary files /dev/null and b/src/wwwroot/amogus/eject/captain-eject.png differ diff --git a/src/wwwroot/amogus/eject/hazmat-eject.png b/src/wwwroot/amogus/eject/hazmat-eject.png new file mode 100644 index 0000000..2f31783 Binary files /dev/null and b/src/wwwroot/amogus/eject/hazmat-eject.png differ diff --git a/src/wwwroot/amogus/eject/mech-eject.png b/src/wwwroot/amogus/eject/mech-eject.png new file mode 100644 index 0000000..bc314e3 Binary files /dev/null and b/src/wwwroot/amogus/eject/mech-eject.png differ diff --git a/src/wwwroot/amogus/eject/military_ejected.png b/src/wwwroot/amogus/eject/military_ejected.png new file mode 100644 index 0000000..b20808e Binary files /dev/null and b/src/wwwroot/amogus/eject/military_ejected.png differ diff --git a/src/wwwroot/amogus/eject/miner-eject.png b/src/wwwroot/amogus/eject/miner-eject.png new file mode 100644 index 0000000..6a3ac2c Binary files /dev/null and b/src/wwwroot/amogus/eject/miner-eject.png differ diff --git a/src/wwwroot/amogus/eject/police_eject.png b/src/wwwroot/amogus/eject/police_eject.png new file mode 100644 index 0000000..f196f78 Binary files /dev/null and b/src/wwwroot/amogus/eject/police_eject.png differ diff --git a/src/wwwroot/amogus/eject/sci-eject.png b/src/wwwroot/amogus/eject/sci-eject.png new file mode 100644 index 0000000..1e53f92 Binary files /dev/null and b/src/wwwroot/amogus/eject/sci-eject.png differ diff --git a/src/wwwroot/amogus/eject/secguard-eject.png b/src/wwwroot/amogus/eject/secguard-eject.png new file mode 100644 index 0000000..192d6d9 Binary files /dev/null and b/src/wwwroot/amogus/eject/secguard-eject.png differ diff --git a/src/wwwroot/amogus/eject/suitBlack-eject.png b/src/wwwroot/amogus/eject/suitBlack-eject.png new file mode 100644 index 0000000..ad319a4 Binary files /dev/null and b/src/wwwroot/amogus/eject/suitBlack-eject.png differ diff --git a/src/wwwroot/amogus/eject/suitWhite-eject.png b/src/wwwroot/amogus/eject/suitWhite-eject.png new file mode 100644 index 0000000..f9b8b10 Binary files /dev/null and b/src/wwwroot/amogus/eject/suitWhite-eject.png differ diff --git a/src/wwwroot/amogus/eject/tarmac-eject.png b/src/wwwroot/amogus/eject/tarmac-eject.png new file mode 100644 index 0000000..93f413b Binary files /dev/null and b/src/wwwroot/amogus/eject/tarmac-eject.png differ diff --git a/src/wwwroot/amogus/eject/wall-eject.png b/src/wwwroot/amogus/eject/wall-eject.png new file mode 100644 index 0000000..bbdbea6 Binary files /dev/null and b/src/wwwroot/amogus/eject/wall-eject.png differ diff --git a/src/wwwroot/amogus/eject/winter-eject.png b/src/wwwroot/amogus/eject/winter-eject.png new file mode 100644 index 0000000..df83c16 Binary files /dev/null and b/src/wwwroot/amogus/eject/winter-eject.png differ diff --git a/src/wwwroot/amogus/idle/archae-idle.png b/src/wwwroot/amogus/idle/archae-idle.png new file mode 100644 index 0000000..e5c9151 Binary files /dev/null and b/src/wwwroot/amogus/idle/archae-idle.png differ diff --git a/src/wwwroot/amogus/idle/astro-idle.png b/src/wwwroot/amogus/idle/astro-idle.png new file mode 100644 index 0000000..73ddaee Binary files /dev/null and b/src/wwwroot/amogus/idle/astro-idle.png differ diff --git a/src/wwwroot/amogus/idle/captain-idle.png b/src/wwwroot/amogus/idle/captain-idle.png new file mode 100644 index 0000000..7967ce0 Binary files /dev/null and b/src/wwwroot/amogus/idle/captain-idle.png differ diff --git a/src/wwwroot/amogus/idle/hazmat-idle.png b/src/wwwroot/amogus/idle/hazmat-idle.png new file mode 100644 index 0000000..572b9ff Binary files /dev/null and b/src/wwwroot/amogus/idle/hazmat-idle.png differ diff --git a/src/wwwroot/amogus/idle/mech-idle.png b/src/wwwroot/amogus/idle/mech-idle.png new file mode 100644 index 0000000..29ec8a9 Binary files /dev/null and b/src/wwwroot/amogus/idle/mech-idle.png differ diff --git a/src/wwwroot/amogus/idle/military-idle.png b/src/wwwroot/amogus/idle/military-idle.png new file mode 100644 index 0000000..8e6f629 Binary files /dev/null and b/src/wwwroot/amogus/idle/military-idle.png differ diff --git a/src/wwwroot/amogus/idle/miner-idle.png b/src/wwwroot/amogus/idle/miner-idle.png new file mode 100644 index 0000000..ef5a798 Binary files /dev/null and b/src/wwwroot/amogus/idle/miner-idle.png differ diff --git a/src/wwwroot/amogus/idle/pol-idle.png b/src/wwwroot/amogus/idle/pol-idle.png new file mode 100644 index 0000000..4d088c5 Binary files /dev/null and b/src/wwwroot/amogus/idle/pol-idle.png differ diff --git a/src/wwwroot/amogus/idle/sci-idle.png b/src/wwwroot/amogus/idle/sci-idle.png new file mode 100644 index 0000000..ef4a7c2 Binary files /dev/null and b/src/wwwroot/amogus/idle/sci-idle.png differ diff --git a/src/wwwroot/amogus/idle/secguard-idle.png b/src/wwwroot/amogus/idle/secguard-idle.png new file mode 100644 index 0000000..f8dbabf Binary files /dev/null and b/src/wwwroot/amogus/idle/secguard-idle.png differ diff --git a/src/wwwroot/amogus/idle/suitBlack-idle.png b/src/wwwroot/amogus/idle/suitBlack-idle.png new file mode 100644 index 0000000..a8a482b Binary files /dev/null and b/src/wwwroot/amogus/idle/suitBlack-idle.png differ diff --git a/src/wwwroot/amogus/idle/suitWhite-idle.png b/src/wwwroot/amogus/idle/suitWhite-idle.png new file mode 100644 index 0000000..d366634 Binary files /dev/null and b/src/wwwroot/amogus/idle/suitWhite-idle.png differ diff --git a/src/wwwroot/amogus/idle/tarmac-idle.png b/src/wwwroot/amogus/idle/tarmac-idle.png new file mode 100644 index 0000000..4705e24 Binary files /dev/null and b/src/wwwroot/amogus/idle/tarmac-idle.png differ diff --git a/src/wwwroot/amogus/idle/wall-idle.png b/src/wwwroot/amogus/idle/wall-idle.png new file mode 100644 index 0000000..49c80b6 Binary files /dev/null and b/src/wwwroot/amogus/idle/wall-idle.png differ diff --git a/src/wwwroot/amogus/idle/winter-idle.png b/src/wwwroot/amogus/idle/winter-idle.png new file mode 100644 index 0000000..7a080b0 Binary files /dev/null and b/src/wwwroot/amogus/idle/winter-idle.png differ diff --git a/src/wwwroot/imagemanip/templates/fortuneteller.jpg b/src/wwwroot/imagemanip/templates/fortuneteller.jpg new file mode 100644 index 0000000..4d2d369 Binary files /dev/null and b/src/wwwroot/imagemanip/templates/fortuneteller.jpg differ