Add amogus generator & fortune teller meme

main
exsersewo 2 years ago
parent 2da300a197
commit 42c9abba3f
Signed by: exsersewo
GPG Key ID: 9C6DDF9AC9BA14A0
  1. 44
      src/Controllers/AmogusController.cs
  2. 22
      src/Extensions.cs
  3. 579
      src/Helpers/AmogusHelper.cs
  4. 3
      src/Helpers/StreamResult.cs
  5. 144
      src/Managers/ImageManager.cs
  6. 2
      src/Program.cs
  7. 4
      src/Properties/launchSettings.json
  8. 18
      src/appsettings.json
  9. BIN
      src/wwwroot/amogus/common/Dead0001.png
  10. BIN
      src/wwwroot/amogus/common/Dead0040.png
  11. BIN
      src/wwwroot/amogus/common/SpacemanLarge.png
  12. BIN
      src/wwwroot/amogus/common/Stars.png
  13. BIN
      src/wwwroot/amogus/common/ejected.png
  14. BIN
      src/wwwroot/amogus/common/ghostbob0001.png
  15. BIN
      src/wwwroot/amogus/common/idle.png
  16. BIN
      src/wwwroot/amogus/common/noshadow.png
  17. BIN
      src/wwwroot/amogus/eject/archae-eject.png
  18. BIN
      src/wwwroot/amogus/eject/astro-eject.png
  19. BIN
      src/wwwroot/amogus/eject/captain-eject.png
  20. BIN
      src/wwwroot/amogus/eject/hazmat-eject.png
  21. BIN
      src/wwwroot/amogus/eject/mech-eject.png
  22. BIN
      src/wwwroot/amogus/eject/military_ejected.png
  23. BIN
      src/wwwroot/amogus/eject/miner-eject.png
  24. BIN
      src/wwwroot/amogus/eject/police_eject.png
  25. BIN
      src/wwwroot/amogus/eject/sci-eject.png
  26. BIN
      src/wwwroot/amogus/eject/secguard-eject.png
  27. BIN
      src/wwwroot/amogus/eject/suitBlack-eject.png
  28. BIN
      src/wwwroot/amogus/eject/suitWhite-eject.png
  29. BIN
      src/wwwroot/amogus/eject/tarmac-eject.png
  30. BIN
      src/wwwroot/amogus/eject/wall-eject.png
  31. BIN
      src/wwwroot/amogus/eject/winter-eject.png
  32. BIN
      src/wwwroot/amogus/idle/archae-idle.png
  33. BIN
      src/wwwroot/amogus/idle/astro-idle.png
  34. BIN
      src/wwwroot/amogus/idle/captain-idle.png
  35. BIN
      src/wwwroot/amogus/idle/hazmat-idle.png
  36. BIN
      src/wwwroot/amogus/idle/mech-idle.png
  37. BIN
      src/wwwroot/amogus/idle/military-idle.png
  38. BIN
      src/wwwroot/amogus/idle/miner-idle.png
  39. BIN
      src/wwwroot/amogus/idle/pol-idle.png
  40. BIN
      src/wwwroot/amogus/idle/sci-idle.png
  41. BIN
      src/wwwroot/amogus/idle/secguard-idle.png
  42. BIN
      src/wwwroot/amogus/idle/suitBlack-idle.png
  43. BIN
      src/wwwroot/amogus/idle/suitWhite-idle.png
  44. BIN
      src/wwwroot/amogus/idle/tarmac-idle.png
  45. BIN
      src/wwwroot/amogus/idle/wall-idle.png
  46. BIN
      src/wwwroot/amogus/idle/winter-idle.png
  47. BIN
      src/wwwroot/imagemanip/templates/fortuneteller.jpg

@ -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
}

@ -32,13 +32,14 @@ public class StreamResult : ActionResult, IStatusCodeActionResult, IActionResult
? ContentType ? ContentType
: "image/png"; : "image/png";
Value.Position = 0;
await Value.CopyToAsync(response.Body); await Value.CopyToAsync(response.Body);
await Value.FlushAsync(); await Value.FlushAsync();
await Value.DisposeAsync(); await Value.DisposeAsync();
context.HttpContext.Response.RegisterForDisposeAsync(Value); context.HttpContext.Response.RegisterForDisposeAsync(Value);
GC.Collect(); GC.Collect();

@ -1,3 +1,4 @@
using System.Numerics;
using System.Text; using System.Text;
using ImageMagick; using ImageMagick;
using Kynareth.Models; using Kynareth.Models;
@ -15,8 +16,28 @@ public static partial class ImageManager
private static ObjectResult GetResult(int statusCode, object data) private static ObjectResult GetResult(int statusCode, object data)
=> new(data) { StatusCode = statusCode }; => 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() using var label = new MagickImage($"caption:{text}", new MagickReadSettings()
{ {
@ -28,8 +49,8 @@ public static partial class ImageManager
FontPointsize = fontSize, FontPointsize = fontSize,
Font = font, Font = font,
TextEncoding = encoding, TextEncoding = encoding,
StrokeColor = MagickColors.Black, StrokeColor = new MagickColor(strokeColor ?? MagickColors.Black),
StrokeWidth = 2 StrokeWidth = strokeWidth
}); });
image.Composite(label, rect.X, rect.Y, CompositeOperator.Over); image.Composite(label, rect.X, rect.Y, CompositeOperator.Over);
@ -37,22 +58,125 @@ public static partial class ImageManager
return image; 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, Width = image.Width,
Height = rect.Height, Height = image.Height,
BackgroundColor = MagickColors.Transparent, BackgroundColor = MagickColors.Transparent,
FillColor = fontColor, FillColor = fontColor,
TextGravity = Gravity.Center, TextGravity = Gravity.Center,
Font = font, Font = font,
TextEncoding = encoding, TextEncoding = encoding,
StrokeColor = MagickColors.Black, StrokeColor = new MagickColor(strokeColor ?? MagickColors.Black),
StrokeWidth = 2 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; return image;
} }

@ -1,4 +1,5 @@
using Exsersewo.Common.Utilities; using Exsersewo.Common.Utilities;
using Kynareth.Helpers;
using Kynareth.Managers; using Kynareth.Managers;
using Kynareth.Models; using Kynareth.Models;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@ -32,6 +33,7 @@ builder.Services.AddDbContext<KynarethDbContext>(options =>
var app = builder.Build(); var app = builder.Build();
ImageManager.Configure(app.Configuration, app.Environment); ImageManager.Configure(app.Configuration, app.Environment);
AmogusHelper.Configure(app.Environment);
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.
//if (app.Environment.IsDevelopment()) //if (app.Environment.IsDevelopment())

@ -14,7 +14,7 @@
"dotnetRunMessages": true, "dotnetRunMessages": true,
"launchBrowser": true, "launchBrowser": true,
"launchUrl": "swagger", "launchUrl": "swagger",
"applicationUrl": "http://localhost:5054", "applicationUrl": "http://localhost:5000",
"environmentVariables": { "environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development" "ASPNETCORE_ENVIRONMENT": "Development"
} }
@ -24,7 +24,7 @@
"dotnetRunMessages": true, "dotnetRunMessages": true,
"launchBrowser": true, "launchBrowser": true,
"launchUrl": "swagger", "launchUrl": "swagger",
"applicationUrl": "https://localhost:7072;http://localhost:5054", "applicationUrl": "https://localhost:7072;http://localhost:5000",
"environmentVariables": { "environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development" "ASPNETCORE_ENVIRONMENT": "Development"
} }

@ -1705,6 +1705,24 @@
"h": 75 "h": 75
} }
] ]
},
{
"name": "fortune",
"image": "fortuneteller.jpg",
"textpositions": [
{
"x": 130,
"y": 413,
"w": 101,
"h": 67
},
{
"x": 505,
"y": 455,
"w": 127,
"h": 66
}
]
} }
] ]
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Loading…
Cancel
Save