From 099ae62c0bc47d92bf3f298ffd90f7e7b457e117 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Sun, 15 Oct 2017 11:48:29 +0200 Subject: [PATCH] Added .rip command again :^) --- .../Common/Replacements/ReplacementBuilder.cs | 66 ++++++------- .../Services/PlayingRotateService.cs | 6 +- NadekoBot.Core/Modules/Searches/Searches.cs | 16 ++++ .../Searches/Services/SearchesService.cs | 74 ++++++++++++++- .../Modules/Xp/Services/XpService.cs | 93 ++++--------------- NadekoBot.Core/Services/IImagesService.cs | 3 + NadekoBot.Core/Services/Impl/FontProvider.cs | 38 ++++++++ NadekoBot.Core/Services/Impl/ImagesService.cs | 9 ++ NadekoBot.Core/_Extensions/Extensions.cs | 36 +++++++ docs/guides/Windows Guide.md | 2 +- 10 files changed, 229 insertions(+), 114 deletions(-) create mode 100644 NadekoBot.Core/Services/Impl/FontProvider.cs diff --git a/NadekoBot.Core/Common/Replacements/ReplacementBuilder.cs b/NadekoBot.Core/Common/Replacements/ReplacementBuilder.cs index 1d59d5b6..ad6a2710 100644 --- a/NadekoBot.Core/Common/Replacements/ReplacementBuilder.cs +++ b/NadekoBot.Core/Common/Replacements/ReplacementBuilder.cs @@ -6,6 +6,8 @@ using Discord; using Discord.Commands; using Discord.WebSocket; using NadekoBot.Extensions; +using NadekoBot.Modules.Music.Services; +using NadekoBot.Modules.Administration.Services; namespace NadekoBot.Common.Replacements { @@ -44,19 +46,19 @@ namespace NadekoBot.Common.Replacements _reps.TryAdd("%sid%", () => g == null ? "DM" : g.Id.ToString()); _reps.TryAdd("%server%", () => g == null ? "DM" : g.Name); - //_reps.TryAdd("%server_time%", () => - //{ - // TimeZoneInfo to = TimeZoneInfo.Local; - // if (g != null) - // { - // if (GuildTimezoneService.AllServices.TryGetValue(client.CurrentUser.Id, out var tz)) - // to = tz.GetTimeZoneOrDefault(g.Id) ?? TimeZoneInfo.Local; - // } + _reps.TryAdd("%server_time%", () => + { + TimeZoneInfo to = TimeZoneInfo.Local; + if (g != null) + { + if (GuildTimezoneService.AllServices.TryGetValue(client.CurrentUser.Id, out var tz)) + to = tz.GetTimeZoneOrDefault(g.Id) ?? TimeZoneInfo.Local; + } - // return TimeZoneInfo.ConvertTime(DateTime.UtcNow, - // TimeZoneInfo.Utc, - // to).ToString("HH:mm ") + to.StandardName.GetInitials(); - //}); + return TimeZoneInfo.ConvertTime(DateTime.UtcNow, + TimeZoneInfo.Utc, + to).ToString("HH:mm ") + to.StandardName.GetInitials(); + }); return this; } @@ -86,26 +88,26 @@ namespace NadekoBot.Common.Replacements return this; } - //public ReplacementBuilder WithMusic(MusicService ms) - //{ - // _reps.TryAdd("%playing%", () => - // { - // var cnt = ms.MusicPlayers.Count(kvp => kvp.Value.Current.Current != null); - // if (cnt != 1) return cnt.ToString(); - // try - // { - // var mp = ms.MusicPlayers.FirstOrDefault(); - // var title = mp.Value?.Current.Current?.Title; - // return title ?? "No songs"; - // } - // catch - // { - // return "error"; - // } - // }); - // _reps.TryAdd("%queued%", () => ms.MusicPlayers.Sum(kvp => kvp.Value.QueueArray().Songs.Length).ToString()); - // return this; - //} + public ReplacementBuilder WithMusic(MusicService ms) + { + _reps.TryAdd("%playing%", () => + { + var cnt = ms.MusicPlayers.Count(kvp => kvp.Value.Current.Current != null); + if (cnt != 1) return cnt.ToString(); + try + { + var mp = ms.MusicPlayers.FirstOrDefault(); + var title = mp.Value?.Current.Current?.Title; + return title ?? "No songs"; + } + catch + { + return "error"; + } + }); + _reps.TryAdd("%queued%", () => ms.MusicPlayers.Sum(kvp => kvp.Value.QueueArray().Songs.Length).ToString()); + return this; + } public ReplacementBuilder WithRngRegex() { diff --git a/NadekoBot.Core/Modules/Administration/Services/PlayingRotateService.cs b/NadekoBot.Core/Modules/Administration/Services/PlayingRotateService.cs index 476666ff..50d222c2 100644 --- a/NadekoBot.Core/Modules/Administration/Services/PlayingRotateService.cs +++ b/NadekoBot.Core/Modules/Administration/Services/PlayingRotateService.cs @@ -6,6 +6,7 @@ using NadekoBot.Common.Replacements; using NadekoBot.Core.Services; using NadekoBot.Core.Services.Database.Models; using NLog; +using NadekoBot.Modules.Music.Services; namespace NadekoBot.Modules.Administration.Services { @@ -27,7 +28,7 @@ namespace NadekoBot.Modules.Administration.Services } public PlayingRotateService(DiscordSocketClient client, IBotConfigProvider bcp, - DbService db, IDataCache cache, NadekoBot bot) + DbService db, IDataCache cache, NadekoBot bot, MusicService music) { _client = client; _bcp = bcp; @@ -41,8 +42,7 @@ namespace NadekoBot.Modules.Administration.Services _rep = new ReplacementBuilder() .WithClient(client) .WithStats(client) - //todo how to add music to replacement builder? - //.WithMusic(music) + .WithMusic(music) .Build(); _t = new Timer(async (objState) => diff --git a/NadekoBot.Core/Modules/Searches/Searches.cs b/NadekoBot.Core/Modules/Searches/Searches.cs index 9ef9af9b..083197d1 100644 --- a/NadekoBot.Core/Modules/Searches/Searches.cs +++ b/NadekoBot.Core/Modules/Searches/Searches.cs @@ -36,6 +36,22 @@ namespace NadekoBot.Modules.Searches _google = google; } + //for anonymasen :^) + [NadekoCommand, Usage, Description, Aliases] + public async Task Rip([Remainder]IGuildUser usr) + { + using (var pic = await _service.GetRipPictureAsync(usr.Nickname ?? usr.Username, usr.RealAvatarUrl())) + using (var picStream = pic.ToStream()) + { + await Context.Channel.SendFileAsync( + picStream, + "rip.png", + $"Rip {Format.Bold(usr.ToString())} \n\t- " + + Format.Italics(Context.User.ToString())) + .ConfigureAwait(false); + } + } + [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.ManageMessages)] diff --git a/NadekoBot.Core/Modules/Searches/Services/SearchesService.cs b/NadekoBot.Core/Modules/Searches/Services/SearchesService.cs index 602396b8..f16c9fa6 100644 --- a/NadekoBot.Core/Modules/Searches/Services/SearchesService.cs +++ b/NadekoBot.Core/Modules/Searches/Services/SearchesService.cs @@ -18,6 +18,11 @@ using Newtonsoft.Json.Linq; using AngleSharp; using System.Threading; using NadekoBot.Modules.Searches.Exceptions; +using ImageSharp; +using Image = ImageSharp.Image; +using SixLabors.Primitives; +using SixLabors.Fonts; +using NadekoBot.Core.Services.Impl; namespace NadekoBot.Modules.Searches.Services { @@ -29,11 +34,16 @@ namespace NadekoBot.Modules.Searches.Services private readonly IGoogleApiService _google; private readonly DbService _db; private readonly Logger _log; + private readonly IImagesService _imgs; + private readonly IDataCache _cache; + private readonly FontProvider _fonts; + private readonly HttpClient http; public ConcurrentDictionary TranslatedChannels { get; } = new ConcurrentDictionary(); public ConcurrentDictionary UserLanguages { get; } = new ConcurrentDictionary(); public readonly string PokemonAbilitiesFile = "data/pokemon/pokemon_abilities7.json"; + public readonly string PokemonListFile = "data/pokemon/pokemon_list7.json"; public Dictionary Pokemons { get; } = new Dictionary(); public Dictionary PokemonAbilities { get; } = new Dictionary(); @@ -50,7 +60,8 @@ namespace NadekoBot.Modules.Searches.Services private readonly ConcurrentDictionary> _blacklistedTags = new ConcurrentDictionary>(); public SearchesService(DiscordSocketClient client, IGoogleApiService google, - DbService db, NadekoBot bot) + DbService db, NadekoBot bot, IImagesService imgs, IDataCache cache, + FontProvider fonts) { Http = new HttpClient(); Http.AddFakeHeaders(); @@ -58,6 +69,10 @@ namespace NadekoBot.Modules.Searches.Services _google = google; _db = db; _log = LogManager.GetCurrentClassLogger(); + _imgs = imgs; + _cache = cache; + _fonts = fonts; + http = new HttpClient(); _blacklistedTags = new ConcurrentDictionary>( bot.AllGuildConfigs.ToDictionary( @@ -126,6 +141,61 @@ namespace NadekoBot.Modules.Searches.Services _log.Warn("data/magicitems.json is missing. Magic items are not loaded."); } + public async Task> GetRipPictureAsync(string text, string imgUrl) + { + var (succ, data) = await _cache.TryGetImageDataAsync(imgUrl); + if (!succ) + { + using (var temp = await http.GetAsync(imgUrl, HttpCompletionOption.ResponseHeadersRead)) + { + if (temp.Content.Headers.ContentType.MediaType != "image/png" + && temp.Content.Headers.ContentType.MediaType != "image/jpeg" + && temp.Content.Headers.ContentType.MediaType != "image/gif") + data = null; + else + { + using (var tempDraw = ImageSharp.Image.Load(await temp.Content.ReadAsStreamAsync()).Resize(69, 70)) + { + tempDraw.ApplyRoundedCorners(35); + data = tempDraw.ToStream().ToArray(); + } + } + } + + await _cache.SetImageDataAsync(imgUrl, data); + } + var bg = ImageSharp.Image.Load(_imgs.Rip.ToArray()); + + //avatar 82, 139 + if (data != null) + { + var avatar = Image.Load(data).Resize(85, 85); + bg.DrawImage(avatar, + default, + new Point(82, 139), + GraphicsOptions.Default); + } + //text 63, 241 + bg.DrawText(text, + _fonts.RipNameFont, + Rgba32.Black, + new PointF(25, 225), + new ImageSharp.Drawing.TextGraphicsOptions() + { + HorizontalAlignment = HorizontalAlignment.Center, + WrapTextWidth = 190, + }); + + //flowa + var flowers = Image.Load(_imgs.FlowerCircle.ToArray()); + bg.DrawImage(flowers, + default, + new Point(0, 0), + GraphicsOptions.Default); + + return bg; + } + public async Task Translate(string langs, string text = null) { var langarr = langs.ToLowerInvariant().Split('>'); @@ -212,7 +282,7 @@ namespace NadekoBot.Modules.Searches.Services public async Task<(string Text, string BaseUri)> GetRandomJoke() { - var config = Configuration.Default.WithDefaultLoader(); + var config = AngleSharp.Configuration.Default.WithDefaultLoader(); var document = await BrowsingContext.New(config).OpenAsync("http://www.goodbadjokes.com/random"); var html = document.QuerySelector(".post > .joke-content"); diff --git a/NadekoBot.Core/Modules/Xp/Services/XpService.cs b/NadekoBot.Core/Modules/Xp/Services/XpService.cs index 2c4314c1..ef3bdbcc 100644 --- a/NadekoBot.Core/Modules/Xp/Services/XpService.cs +++ b/NadekoBot.Core/Modules/Xp/Services/XpService.cs @@ -37,7 +37,7 @@ namespace NadekoBot.Modules.Xp.Services private readonly Logger _log; private readonly NadekoStrings _strings; private readonly IDataCache _cache; - private readonly FontCollection _fonts = new FontCollection(); + private readonly FontProvider _fonts; public const int XP_REQUIRED_LVL_1 = 36; private readonly ConcurrentDictionary> _excludedRoles @@ -59,17 +59,11 @@ namespace NadekoBot.Modules.Xp.Services private readonly CancellationTokenSource _clearRewardTimerTokenSource; private readonly Task _clearRewardTimer; private readonly HttpClient http = new HttpClient(); - private FontFamily _usernameFontFamily; - private FontFamily _clubFontFamily; - private Font _levelFont; - private Font _xpFont; - private Font _awardedFont; - private Font _rankFont; - private Font _timeFont; public XpService(CommandHandler cmd, IBotConfigProvider bc, NadekoBot bot, IImagesService images, - DbService db, NadekoStrings strings, IDataCache cache) + DbService db, NadekoStrings strings, IDataCache cache, + FontProvider fonts) { _db = db; _cmd = cmd; @@ -78,6 +72,7 @@ namespace NadekoBot.Modules.Xp.Services _log = LogManager.GetCurrentClassLogger(); _strings = strings; _cache = cache; + _fonts = fonts; //load settings var allGuildConfigs = bot.AllGuildConfigs.Where(x => x.XpSettings != null); @@ -105,16 +100,6 @@ namespace NadekoBot.Modules.Xp.Services allGuildConfigs.Where(x => x.XpSettings.ServerExcluded) .Select(x => x.GuildId)); - //todo 60 move to font provider or somethign - _fonts = new FontCollection(); - if (Directory.Exists("data/fonts")) - foreach (var file in Directory.GetFiles("data/fonts")) - { - _fonts.Install(file); - } - - InitializeFonts(); - _cmd.OnMessageNoTrigger += _cmd_OnMessageNoTrigger; _updateXpTimer = new Timer(async _ => @@ -547,16 +532,6 @@ namespace NadekoBot.Modules.Xp.Services return GenerateImageAsync(GetUserStats(user)); } - private void InitializeFonts() - { - _usernameFontFamily = _fonts.Find("Whitney-Bold"); - _clubFontFamily = _fonts.Find("Whitney-Bold"); - _levelFont = _fonts.Find("Whitney-Bold").CreateFont(45); - _xpFont = _fonts.Find("Whitney-Bold").CreateFont(50); - _awardedFont = _fonts.Find("Whitney-Bold").CreateFont(25); - _rankFont = _fonts.Find("Uni Sans Thin CAPS").CreateFont(30); - _timeFont = _fonts.Find("Whitney-Bold").CreateFont(20); - } public Task GenerateImageAsync(FullUserStats stats) => Task.Run(async () => { @@ -564,7 +539,7 @@ namespace NadekoBot.Modules.Xp.Services { var username = stats.User.ToString(); - var usernameFont = _usernameFontFamily + var usernameFont = _fonts.UsernameFontFamily .CreateFont(username.Length <= 6 ? 50 : 50 - username.Length); @@ -574,17 +549,17 @@ namespace NadekoBot.Modules.Xp.Services // level - img.DrawText(stats.Global.Level.ToString(), _levelFont, Rgba32.White, + img.DrawText(stats.Global.Level.ToString(), _fonts.LevelFont, Rgba32.White, new PointF(47, 137)); - img.DrawText(stats.Guild.Level.ToString(), _levelFont, Rgba32.White, + img.DrawText(stats.Guild.Level.ToString(), _fonts.LevelFont, Rgba32.White, new PointF(47, 285)); //club name var clubName = stats.User.Club?.ToString() ?? "-"; - var clubFont = _clubFontFamily + var clubFont = _fonts.ClubFontFamily .CreateFont(clubName.Length <= 8 ? 35 : 35 - (clubName.Length / 2)); @@ -607,7 +582,7 @@ namespace NadekoBot.Modules.Xp.Services new PointF(286 + (450 * (global.LevelXp / (float)global.RequiredXp)), 235), new PointF(286, 235), }); - img.DrawText($"{global.LevelXp}/{global.RequiredXp}", _xpFont, brush, pen, + img.DrawText($"{global.LevelXp}/{global.RequiredXp}", _fonts.XpFont, brush, pen, new PointF(430, 130)); img.FillPolygon(xpBgBrush, new[] { @@ -616,7 +591,7 @@ namespace NadekoBot.Modules.Xp.Services new PointF(247 + (450 * (guild.LevelXp / (float)guild.RequiredXp)), 379), new PointF(247, 379), }); - img.DrawText($"{guild.LevelXp}/{guild.RequiredXp}", _xpFont, brush, pen, + img.DrawText($"{guild.LevelXp}/{guild.RequiredXp}", _fonts.XpFont, brush, pen, new PointF(400, 270)); if (stats.FullGuildStats.AwardedXp != 0) @@ -624,16 +599,16 @@ namespace NadekoBot.Modules.Xp.Services var sign = stats.FullGuildStats.AwardedXp > 0 ? "+ " : ""; - img.DrawText($"({sign}{stats.FullGuildStats.AwardedXp})", _awardedFont, brush, pen, + img.DrawText($"({sign}{stats.FullGuildStats.AwardedXp})", _fonts.AwardedFont, brush, pen, new PointF(445 - (Math.Max(0, (stats.FullGuildStats.AwardedXp.ToString().Length - 2)) * 5), 335)); } //ranking - img.DrawText(stats.GlobalRanking.ToString(), _rankFont, Rgba32.White, + img.DrawText(stats.GlobalRanking.ToString(), _fonts.RankFont, Rgba32.White, new PointF(148, 170)); - img.DrawText(stats.GuildRanking.ToString(), _rankFont, Rgba32.White, + img.DrawText(stats.GuildRanking.ToString(), _fonts.RankFont, Rgba32.White, new PointF(148, 317)); //time on this level @@ -644,10 +619,10 @@ namespace NadekoBot.Modules.Xp.Services return $"{offset.Days}d{offset.Hours}h{offset.Minutes}m"; } - img.DrawText(GetTimeSpent(stats.User.LastLevelUp), _timeFont, Rgba32.White, + img.DrawText(GetTimeSpent(stats.User.LastLevelUp), _fonts.TimeFont, Rgba32.White, new PointF(50, 197)); - img.DrawText(GetTimeSpent(stats.FullGuildStats.LastLevelUp), _timeFont, Rgba32.White, + img.DrawText(GetTimeSpent(stats.FullGuildStats.LastLevelUp), _fonts.TimeFont, Rgba32.White, new PointF(50, 344)); //avatar @@ -664,7 +639,7 @@ namespace NadekoBot.Modules.Xp.Services using (var temp = await http.GetStreamAsync(avatarUrl)) using (var tempDraw = Image.Load(temp).Resize(69, 70)) { - ApplyRoundedCorners(tempDraw, 35); + tempDraw.ApplyRoundedCorners(35); data = tempDraw.ToStream().ToArray(); } @@ -710,7 +685,7 @@ namespace NadekoBot.Modules.Xp.Services return; using (var tempDraw = Image.Load(await temp.Content.ReadAsStreamAsync()).Resize(45, 45)) { - ApplyRoundedCorners(tempDraw, 22.5f); + tempDraw.ApplyRoundedCorners(22.5f); data = tempDraw.ToStream().ToArray(); } } @@ -731,40 +706,6 @@ namespace NadekoBot.Modules.Xp.Services } } - // https://github.com/SixLabors/ImageSharp/tree/master/samples/AvatarWithRoundedCorner - public static void ApplyRoundedCorners(Image img, float cornerRadius) - { - var corners = BuildCorners(img.Width, img.Height, cornerRadius); - // now we have our corners time to draw them - img.Fill(Rgba32.Transparent, corners, new GraphicsOptions(true) - { - BlenderMode = ImageSharp.PixelFormats.PixelBlenderMode.Src // enforces that any part of this shape that has color is punched out of the background - }); - } - - public static IPathCollection BuildCorners(int imageWidth, int imageHeight, float cornerRadius) - { - // first create a square - var rect = new RectangularePolygon(-0.5f, -0.5f, cornerRadius, cornerRadius); - - // then cut out of the square a circle so we are left with a corner - var cornerToptLeft = rect.Clip(new EllipsePolygon(cornerRadius - 0.5f, cornerRadius - 0.5f, cornerRadius)); - - // corner is now a corner shape positions top left - //lets make 3 more positioned correctly, we can do that by translating the orgional around the center of the image - var center = new Vector2(imageWidth / 2, imageHeight / 2); - - float rightPos = imageWidth - cornerToptLeft.Bounds.Width + 1; - float bottomPos = imageHeight - cornerToptLeft.Bounds.Height + 1; - - // move it across the width of the image - the width of the shape - var cornerTopRight = cornerToptLeft.RotateDegree(90).Translate(rightPos, 0); - var cornerBottomLeft = cornerToptLeft.RotateDegree(-90).Translate(0, bottomPos); - var cornerBottomRight = cornerToptLeft.RotateDegree(180).Translate(rightPos, bottomPos); - - return new PathCollection(cornerToptLeft, cornerBottomLeft, cornerTopRight, cornerBottomRight); - } - public Task Unload() { _cmd.OnMessageNoTrigger -= _cmd_OnMessageNoTrigger; diff --git a/NadekoBot.Core/Services/IImagesService.cs b/NadekoBot.Core/Services/IImagesService.cs index 53458808..0d700c42 100644 --- a/NadekoBot.Core/Services/IImagesService.cs +++ b/NadekoBot.Core/Services/IImagesService.cs @@ -19,6 +19,9 @@ namespace NadekoBot.Core.Services ImmutableArray XpCard { get; } + ImmutableArray Rip { get; } + ImmutableArray FlowerCircle { get; } + void Reload(); } } diff --git a/NadekoBot.Core/Services/Impl/FontProvider.cs b/NadekoBot.Core/Services/Impl/FontProvider.cs new file mode 100644 index 00000000..9497cc8b --- /dev/null +++ b/NadekoBot.Core/Services/Impl/FontProvider.cs @@ -0,0 +1,38 @@ +using SixLabors.Fonts; +using System.IO; + +namespace NadekoBot.Core.Services.Impl +{ + public class FontProvider : INService + { + private readonly FontCollection _fonts; + + public FontProvider() + { + _fonts = new FontCollection(); + if (Directory.Exists("data/fonts")) + foreach (var file in Directory.GetFiles("data/fonts")) + { + _fonts.Install(file); + } + + UsernameFontFamily = _fonts.Find("Whitney-Bold"); + ClubFontFamily = _fonts.Find("Whitney-Bold"); + LevelFont = _fonts.Find("Whitney-Bold").CreateFont(45); + XpFont = _fonts.Find("Whitney-Bold").CreateFont(50); + AwardedFont = _fonts.Find("Whitney-Bold").CreateFont(25); + RankFont = _fonts.Find("Uni Sans Thin CAPS").CreateFont(30); + TimeFont = _fonts.Find("Whitney-Bold").CreateFont(20); + RipNameFont = _fonts.Find("Whitney-Bold").CreateFont(20); + } + + public Font LevelFont { get; } + public Font XpFont { get; } + public Font AwardedFont { get; } + public Font RankFont { get; } + public Font TimeFont { get; } + public FontFamily UsernameFontFamily { get; } + public FontFamily ClubFontFamily { get; } + public Font RipNameFont { get; } + } +} diff --git a/NadekoBot.Core/Services/Impl/ImagesService.cs b/NadekoBot.Core/Services/Impl/ImagesService.cs index 8d2e6464..3b8b2a43 100644 --- a/NadekoBot.Core/Services/Impl/ImagesService.cs +++ b/NadekoBot.Core/Services/Impl/ImagesService.cs @@ -27,6 +27,9 @@ namespace NadekoBot.Core.Services.Impl private const string _xpCardPath = _basePath + "xp/xp.png"; + private const string _ripPath = _basePath + "rip/rip.png"; + private const string _ripFlowersPath = _basePath + "rip/rose_overlay.png"; + public ImmutableArray Heads { get; private set; } public ImmutableArray Tails { get; private set; } @@ -44,6 +47,9 @@ namespace NadekoBot.Core.Services.Impl public ImmutableArray XpCard { get; private set; } + public ImmutableArray Rip { get; private set; } + public ImmutableArray FlowerCircle { get; private set; } + public ImagesService() { _log = LogManager.GetCurrentClassLogger(); @@ -82,6 +88,9 @@ namespace NadekoBot.Core.Services.Impl RategirlDot = File.ReadAllBytes(_rategirlDot).ToImmutableArray(); XpCard = File.ReadAllBytes(_xpCardPath).ToImmutableArray(); + + Rip = File.ReadAllBytes(_ripPath).ToImmutableArray(); + FlowerCircle = File.ReadAllBytes(_ripFlowersPath).ToImmutableArray(); } catch (Exception ex) { diff --git a/NadekoBot.Core/_Extensions/Extensions.cs b/NadekoBot.Core/_Extensions/Extensions.cs index 48170cce..ff22382a 100644 --- a/NadekoBot.Core/_Extensions/Extensions.cs +++ b/NadekoBot.Core/_Extensions/Extensions.cs @@ -17,11 +17,47 @@ using NadekoBot.Common.Collections; using SixLabors.Primitives; using NadekoBot.Common; using NadekoBot.Core.Services; +using SixLabors.Shapes; +using System.Numerics; namespace NadekoBot.Extensions { public static class Extensions { + // https://github.com/SixLabors/ImageSharp/tree/master/samples/AvatarWithRoundedCorner + public static void ApplyRoundedCorners(this Image img, float cornerRadius) + { + var corners = BuildCorners(img.Width, img.Height, cornerRadius); + // now we have our corners time to draw them + img.Fill(Rgba32.Transparent, corners, new GraphicsOptions(true) + { + BlenderMode = ImageSharp.PixelFormats.PixelBlenderMode.Src // enforces that any part of this shape that has color is punched out of the background + }); + } + + public static IPathCollection BuildCorners(int imageWidth, int imageHeight, float cornerRadius) + { + // first create a square + var rect = new RectangularePolygon(-0.5f, -0.5f, cornerRadius, cornerRadius); + + // then cut out of the square a circle so we are left with a corner + var cornerToptLeft = rect.Clip(new EllipsePolygon(cornerRadius - 0.5f, cornerRadius - 0.5f, cornerRadius)); + + // corner is now a corner shape positions top left + //lets make 3 more positioned correctly, we can do that by translating the orgional around the center of the image + var center = new Vector2(imageWidth / 2, imageHeight / 2); + + float rightPos = imageWidth - cornerToptLeft.Bounds.Width + 1; + float bottomPos = imageHeight - cornerToptLeft.Bounds.Height + 1; + + // move it across the width of the image - the width of the shape + var cornerTopRight = cornerToptLeft.RotateDegree(90).Translate(rightPos, 0); + var cornerBottomLeft = cornerToptLeft.RotateDegree(-90).Translate(0, bottomPos); + var cornerBottomRight = cornerToptLeft.RotateDegree(180).Translate(rightPos, bottomPos); + + return new PathCollection(cornerToptLeft, cornerBottomLeft, cornerTopRight, cornerBottomRight); + } + public static string RedisKey(this IBotCredentials bc) { return bc.Token.Substring(0, 10); diff --git a/docs/guides/Windows Guide.md b/docs/guides/Windows Guide.md index fdd3b0f0..c426d981 100644 --- a/docs/guides/Windows Guide.md +++ b/docs/guides/Windows Guide.md @@ -7,7 +7,7 @@ #### Guide - Download and run the [NadekoBot Updater.][Updater] -- Press **`Install Redis`** then +- Press **`Install Redis`** then - Press **`Install ffmpeg`** and **`Install youtube-dl`** if you want music features. ***NOTE:** RESTART YOUR PC IF YOU DO.* - Press **`Update`** and go through the installation wizard.