@ -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<AmogusController> | 
				
			||||||
 | 
					{ | 
				
			||||||
 | 
						public AmogusController(IConfiguration configuration, ILoggerFactory loggerFactory, IWebHostEnvironment appEnvironment) : base(configuration, loggerFactory, appEnvironment) | 
				
			||||||
 | 
						{ | 
				
			||||||
 | 
						} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[HttpGet("eject")] | 
				
			||||||
 | 
						public async Task<object> 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<object> GetAmongUsRoles() | 
				
			||||||
 | 
							=> Ok(EventResult.FromSuccess(Enum.GetNames<AmogusRole>().Except("None"))); | 
				
			||||||
 | 
						 | 
				
			||||||
 | 
						[HttpGet("colours")] | 
				
			||||||
 | 
						public async Task<object> GetAmongUsColours() | 
				
			||||||
 | 
							=> Ok(EventResult.FromSuccess(Enum.GetNames<AmogusColour>().Except("None"))); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[HttpGet("skins")] | 
				
			||||||
 | 
						public async Task<object> GetAmongUsSkins() | 
				
			||||||
 | 
							=> Ok(EventResult.FromSuccess(Enum.GetNames<AmogusSkin>().Except("None"))); | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,22 @@ | 
				
			|||||||
 | 
					namespace Kynareth; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public static class Extensions | 
				
			||||||
 | 
					{ | 
				
			||||||
 | 
						public static T GetRandomEnumValue<T>() where T : Enum  | 
				
			||||||
 | 
							=> (T) Enum.GetValues(typeof(T)).OfType<Enum>().OrderBy(_ => Guid.NewGuid()).FirstOrDefault(); | 
				
			||||||
 | 
						 | 
				
			||||||
 | 
						public static IList<int> Range(int start, int stop, int step) | 
				
			||||||
 | 
						{ | 
				
			||||||
 | 
							var list = new List<int>(); | 
				
			||||||
 | 
							 | 
				
			||||||
 | 
							for (int i = start; i < stop; i += step) { | 
				
			||||||
 | 
								list.Add(i); | 
				
			||||||
 | 
							} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return list; | 
				
			||||||
 | 
						} | 
				
			||||||
 | 
						 | 
				
			||||||
 | 
						public static T[] Except<T>(this T[] array, T specificValue) where T : IComparable { | 
				
			||||||
 | 
							return array.Where<T>(val => val.CompareTo(specificValue) != 0).ToArray(); | 
				
			||||||
 | 
						} | 
				
			||||||
 | 
					} | 
				
			||||||
@ -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<AmogusColour, MagickColor> _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<AmogusColour, MagickColor> _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<AmogusSkin, string> 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<AmogusSkin, string> 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<AmogusSkin, Point> 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<AmogusSkin, Point> 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"); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// <summary> | 
				
			||||||
 | 
					    /// 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 | 
				
			||||||
 | 
					    /// </summary> | 
				
			||||||
 | 
					    /// <param name="baseImageName">The image to load up</param> | 
				
			||||||
 | 
					    /// <param name="colour">The colour to make the base image, must be one <see cref="AmogusColour"/></param> | 
				
			||||||
 | 
					    /// <returns>A <see cref="MagickImage"/> generated from <paramref name="baseImageName"/></returns> | 
				
			||||||
 | 
					    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; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// <summary> | 
				
			||||||
 | 
					    /// Replaces the Colour of an AmogieCrewMate with the target colour | 
				
			||||||
 | 
					    /// </summary> | 
				
			||||||
 | 
					    /// <param name="image">Image to Colour Replace</param> | 
				
			||||||
 | 
					    /// <param name="colour">Colour to replace with</param> | 
				
			||||||
 | 
					    /// <returns>Colour Replaced Amogie Crewmate</returns> | 
				
			||||||
 | 
					    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; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// <summary> | 
				
			||||||
 | 
					    /// Applies the Eject variant of the Skin | 
				
			||||||
 | 
					    /// </summary> | 
				
			||||||
 | 
					    /// <param name="baseImage">Base Crewmate Image</param> | 
				
			||||||
 | 
					    /// <param name="layerImage">Skin to Apply</param> | 
				
			||||||
 | 
					    /// <param name="idleOffset">Skin offset from <see cref="baseImage"/></param> | 
				
			||||||
 | 
					    /// <param name="baseOrigin">Origin of <see cref="baseImage"/></param> | 
				
			||||||
 | 
					    /// <returns>Crewmate with Skin Applied</returns> | 
				
			||||||
 | 
					    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; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// <summary> | 
				
			||||||
 | 
					    /// Generate a crewmate with the given settings | 
				
			||||||
 | 
					    /// </summary> | 
				
			||||||
 | 
					    /// <param name="colour">Colour of the crewmate</param> | 
				
			||||||
 | 
					    /// <param name="skin">Skin that the crewmate is wearing</param> | 
				
			||||||
 | 
					    /// <param name="ejected">If the crewmate has been ejected or not</param> | 
				
			||||||
 | 
					    /// <returns>Generated crewmate with the given settings</returns> | 
				
			||||||
 | 
					    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; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// <summary> | 
				
			||||||
 | 
					    /// Generate Amongus Stars | 
				
			||||||
 | 
					    /// </summary> | 
				
			||||||
 | 
					    /// <param name="width">Width of the stars</param> | 
				
			||||||
 | 
					    /// <returns>Generated image of stars</returns> | 
				
			||||||
 | 
					    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(); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// <summary> | 
				
			||||||
 | 
					    /// Generates the Ejection Gif with all modifications applied | 
				
			||||||
 | 
					    /// </summary> | 
				
			||||||
 | 
					    /// <param name="name"></param> | 
				
			||||||
 | 
					    /// <param name="colour"></param> | 
				
			||||||
 | 
					    /// <param name="skin"></param> | 
				
			||||||
 | 
					    /// <param name="role"></param> | 
				
			||||||
 | 
					    /// <returns>A <see cref="MemoryStream"/> containing the Amogus Ejected Gif</returns> | 
				
			||||||
 | 
					    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<AmogusRole>(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        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<AmogusColour>(); | 
				
			||||||
 | 
					        if (role is null) role = Extensions.GetRandomEnumValue<AmogusRole>(); | 
				
			||||||
 | 
					        if (skin is null) skin = Extensions.GetRandomEnumValue<AmogusSkin>(); | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        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 | 
				
			||||||
 | 
					} | 
				
			||||||
| 
		 After Width: | Height: | Size: 3.1 KiB  | 
| 
		 After Width: | Height: | Size: 3.0 KiB  | 
| 
		 After Width: | Height: | Size: 7.9 KiB  | 
| 
		 After Width: | Height: | Size: 1.7 KiB  | 
| 
		 After Width: | Height: | Size: 3.8 KiB  | 
| 
		 After Width: | Height: | Size: 4.5 KiB  | 
| 
		 After Width: | Height: | Size: 3.8 KiB  | 
| 
		 After Width: | Height: | Size: 2.4 KiB  | 
| 
		 After Width: | Height: | Size: 2.3 KiB  | 
| 
		 After Width: | Height: | Size: 2.4 KiB  | 
| 
		 After Width: | Height: | Size: 2.2 KiB  | 
| 
		 After Width: | Height: | Size: 2.1 KiB  | 
| 
		 After Width: | Height: | Size: 2.2 KiB  | 
| 
		 After Width: | Height: | Size: 2.2 KiB  | 
| 
		 After Width: | Height: | Size: 2.1 KiB  | 
| 
		 After Width: | Height: | Size: 2.4 KiB  | 
| 
		 After Width: | Height: | Size: 2.8 KiB  | 
| 
		 After Width: | Height: | Size: 2.3 KiB  | 
| 
		 After Width: | Height: | Size: 2.1 KiB  | 
| 
		 After Width: | Height: | Size: 2.3 KiB  | 
| 
		 After Width: | Height: | Size: 2.5 KiB  | 
| 
		 After Width: | Height: | Size: 2.2 KiB  | 
| 
		 After Width: | Height: | Size: 2.8 KiB  | 
| 
		 After Width: | Height: | Size: 1.8 KiB  | 
| 
		 After Width: | Height: | Size: 1.8 KiB  | 
| 
		 After Width: | Height: | Size: 1.6 KiB  | 
| 
		 After Width: | Height: | Size: 1.8 KiB  | 
| 
		 After Width: | Height: | Size: 1.6 KiB  | 
| 
		 After Width: | Height: | Size: 1.7 KiB  | 
| 
		 After Width: | Height: | Size: 1.9 KiB  | 
| 
		 After Width: | Height: | Size: 1.8 KiB  | 
| 
		 After Width: | Height: | Size: 1.7 KiB  | 
| 
		 After Width: | Height: | Size: 1.9 KiB  | 
| 
		 After Width: | Height: | Size: 1.6 KiB  | 
| 
		 After Width: | Height: | Size: 1.7 KiB  | 
| 
		 After Width: | Height: | Size: 2.0 KiB  | 
| 
		 After Width: | Height: | Size: 1.8 KiB  | 
| 
		 After Width: | Height: | Size: 2.6 KiB  | 
| 
		 After Width: | Height: | Size: 85 KiB  |