From 339e71618902d45c52ef123761f4768036c49bb3 Mon Sep 17 00:00:00 2001 From: Lucas Hardt Date: Mon, 10 Aug 2026 10:55:43 +0200 Subject: [PATCH 1/2] Upgrade code for Skiasharp V4 --- Controllers/LockerImageController.cs | 106 +++--- Controllers/ShopImageController.cs | 217 +++++------ Controllers/StatsImageController.cs | 527 ++++++++++++++------------- Controllers/UtilsImageController.cs | 31 +- EasyFortniteStats-ImageApi.csproj | 4 +- ImageUtils.cs | 54 +-- SKPaintSafe.cs | 2 +- 7 files changed, 475 insertions(+), 466 deletions(-) diff --git a/Controllers/LockerImageController.cs b/Controllers/LockerImageController.cs index a0a2e54..73323e5 100644 --- a/Controllers/LockerImageController.cs +++ b/Controllers/LockerImageController.cs @@ -79,23 +79,22 @@ private async Task GenerateImage(Locker locker) using var backgroundPaint = new SKPaintSafe(); backgroundPaint.IsAntialias = true; backgroundPaint.Shader = SKShader.CreateLinearGradient( - new SKPoint((float)imageInfo.Width / 2, 0), - new SKPoint((float)imageInfo.Width / 2, imageInfo.Height), - [new SKColor(44, 154, 234), new SKColor(14, 53, 147)], - [0.0f, 1.0f], - SKShaderTileMode.Repeat); + new SKPoint((float)imageInfo.Width / 2, 0), + new SKPoint((float)imageInfo.Width / 2, imageInfo.Height), + [new SKColor(44, 154, 234), new SKColor(14, 53, 147)], + [0.0f, 1.0f], + SKShaderTileMode.Repeat); canvas.DrawRect(0, 0, imageInfo.Width, imageInfo.Height, backgroundPaint); - var textBounds = new SKRect(); var segoeFont = await assets.GetFont("Assets/Fonts/Segoe.ttf"); // don't dispose var iconBitmap = await assets.GetBitmap("Assets/Images/Locker/Icon.png"); // don't dispose var resize = (int)(50 * uiResizingFactor); using var drawIconPaint = new SKPaint(); drawIconPaint.IsAntialias = true; - drawIconPaint.FilterQuality = SKFilterQuality.High; - canvas.DrawBitmap(iconBitmap, SKRect.Create(50, 50, resize, resize), drawIconPaint); + canvas.DrawBitmap(iconBitmap, SKRect.Create(50, 50, resize, resize), + new SKSamplingOptions(SKCubicResampler.Mitchell), drawIconPaint); using var splitPaint = new SKPaint(); splitPaint.IsAntialias = true; @@ -109,15 +108,16 @@ private async Task GenerateImage(Locker locker) using var namePaint = new SKPaint(); namePaint.IsAntialias = true; namePaint.Color = SKColors.White; - namePaint.Typeface = segoeFont; - namePaint.TextSize = nameFontSize; - namePaint.FilterQuality = SKFilterQuality.Medium; + using var namePaintFont = new SKFont(); + namePaintFont.Typeface = segoeFont; + namePaintFont.Size = nameFontSize; - namePaint.MeasureText(locker.PlayerName, ref textBounds); - canvas.DrawText(locker.PlayerName, 50 + resize + splitWidth * 3, 58 - textBounds.Top, namePaint); + namePaintFont.MeasureText(locker.PlayerName, out var textBounds, namePaint); + canvas.DrawText(locker.PlayerName, 50 + resize + splitWidth * 3, 58 - textBounds.Top, + SKTextAlign.Left, namePaintFont, namePaint); using var discordBoxBitmap = await ImageUtils.GenerateDiscordBox(assets, locker.UserName, uiResizingFactor); - canvas.DrawBitmap(discordBoxBitmap, imageInfo.Width - 50 - discordBoxBitmap.Width, 39); + canvas.DrawBitmap(discordBoxBitmap, imageInfo.Width - 50 - discordBoxBitmap.Width, 39, SKSamplingOptions.Default); var column = 0; var row = 0; @@ -126,7 +126,7 @@ private async Task GenerateImage(Locker locker) canvas.DrawBitmap( item.Image, 50 + 256 * column + 25 * column, - 50 + nameFontSize + 50 + row * 313 + row * 25); + 50 + nameFontSize + 50 + row * 313 + row * 25, SKSamplingOptions.Default); column++; if (column != columns) continue; column = 0; @@ -136,7 +136,7 @@ private async Task GenerateImage(Locker locker) // Load Footer.svg file as a stream using var footerBitmap = await GenerateFooter(uiResizingFactor); canvas.DrawBitmap(footerBitmap, (imageInfo.Width - footerBitmap.Width) / 2.0f, - imageInfo.Height - (footerSpace + footerBitmap.Height) / 2.0f); + imageInfo.Height - (footerSpace + footerBitmap.Height) / 2.0f, SKSamplingOptions.Default); return bitmap; } @@ -187,10 +187,9 @@ await Parallel.ForEachAsync(locker.Items, options, async (item, token) => } } - SKBitmap? itemImage = null; - if (itemImageBytes is not null) { + SKBitmap? itemImage; var itemImageRaw = SKBitmap.Decode(itemImageBytes); if (itemImageRaw is null) { @@ -204,7 +203,8 @@ await Parallel.ForEachAsync(locker.Items, options, async (item, token) => fileExists = false; try { - itemImage = itemImageRaw.Resize(new SKImageInfo(256, 256), SKFilterQuality.Medium); + itemImage = itemImageRaw.Resize(new SKImageInfo(256, 256), + new SKSamplingOptions(SKFilterMode.Linear)); } finally { @@ -253,77 +253,73 @@ private async Task GenerateItemCard(LockerItem lockerItem, SKBitmap? i var rarityBackground = await assets.GetBitmap($"Assets/Images/Locker/RarityBackgrounds/{lockerItem.Rarity}.png"); - canvas.DrawBitmap(rarityBackground, SKPoint.Empty); + canvas.DrawBitmap(rarityBackground, SKPoint.Empty, SKSamplingOptions.Default); if (itemImage is not null) { - canvas.DrawBitmap(itemImage, SKPoint.Empty); + canvas.DrawBitmap(itemImage, SKPoint.Empty, SKSamplingOptions.Default); } else { using var questionmarkPaint = new SKPaint(); questionmarkPaint.IsAntialias = true; questionmarkPaint.Color = SKColors.White; - questionmarkPaint.Typeface = await assets.GetFont("Assets/Fonts/Fortnite-86Bold.otf"); - questionmarkPaint.TextSize = 256.0f; - questionmarkPaint.TextAlign = SKTextAlign.Center; + using var questionmarkPaintFont = new SKFont(); + questionmarkPaintFont.Typeface = await assets.GetFont("Assets/Fonts/Fortnite-86Bold.otf"); + questionmarkPaintFont.Size = 256.0f; - var questionmarkTextBounds = new SKRect(); - questionmarkPaint.MeasureText("?", ref questionmarkTextBounds); + questionmarkPaintFont.MeasureText("?", out var questionmarkTextBounds, questionmarkPaint); - canvas.DrawText("?", (float)bitmap.Width / 2, (float)bitmap.Height / 2 + questionmarkTextBounds.Height / 2, - questionmarkPaint); + canvas.DrawText("?", (float)bitmap.Width / 2, (float)bitmap.Height / 2 + questionmarkTextBounds.Height / 2, SKTextAlign.Center, questionmarkPaintFont, questionmarkPaint); } var typeIcon = lockerItem.SourceType != SourceType.Other ? await assets.GetBitmap($"Assets/Images/Locker/Source/{lockerItem.SourceType}.png") : null; using var overlayImage = ImageUtils.GenerateItemCardOverlay(imageInfo.Width, typeIcon); - canvas.DrawBitmap(overlayImage, new SKPoint(0, imageInfo.Height - overlayImage.Height)); + canvas.DrawBitmap(overlayImage, new SKPoint(0, imageInfo.Height - overlayImage.Height), SKSamplingOptions.Default); using var rarityStripe = ImageUtils.GenerateRarityStripe(imageInfo.Width, SKColor.Parse(lockerItem.RarityColor)); canvas.DrawBitmap(rarityStripe, - new SKPoint(0, imageInfo.Height - overlayImage.Height - rarityStripe.Height + 5)); + new SKPoint(0, imageInfo.Height - overlayImage.Height - rarityStripe.Height + 5), SKSamplingOptions.Default); // TODO: Fix Transparency issues var fortniteFont = await assets.GetFont("Assets/Fonts/Fortnite.ttf"); // don't dispose using var namePaint = new SKPaint(); namePaint.IsAntialias = true; - namePaint.TextSize = 18.0f; namePaint.Color = SKColors.White; - namePaint.Typeface = fortniteFont; - namePaint.TextAlign = SKTextAlign.Center; + using var namePaintFont = new SKFont(); + namePaintFont.Size = 18.0f; + namePaintFont.Typeface = fortniteFont; - var entryNameTextBounds = new SKRect(); - namePaint.MeasureText(lockerItem.Name, ref entryNameTextBounds); - canvas.DrawText(lockerItem.Name, (float)bitmap.Width / 2, bitmap.Height - 59 + entryNameTextBounds.Height, - namePaint); + namePaintFont.MeasureText(lockerItem.Name, out var entryNameTextBounds, namePaint); + canvas.DrawText(lockerItem.Name, (float)bitmap.Width / 2, + bitmap.Height - 59 + entryNameTextBounds.Height, SKTextAlign.Center, namePaintFont, namePaint); using var descriptionPaint = new SKPaint(); descriptionPaint.IsAntialias = true; - descriptionPaint.TextSize = 15.0f; descriptionPaint.Color = SKColor.Parse(lockerItem.RarityColor); - descriptionPaint.Typeface = fortniteFont; - descriptionPaint.TextAlign = SKTextAlign.Center; + using var descriptionPaintFont = new SKFont(); + descriptionPaintFont.Size = 15.0f; + descriptionPaintFont.Typeface = fortniteFont; - descriptionPaint.MeasureText(lockerItem.Description, ref entryNameTextBounds); + descriptionPaintFont.MeasureText(lockerItem.Description, out entryNameTextBounds, descriptionPaint); canvas.DrawText(lockerItem.Description, (float)bitmap.Width / 2, - bitmap.Height - 42 + entryNameTextBounds.Height, descriptionPaint); + bitmap.Height - 42 + entryNameTextBounds.Height, SKTextAlign.Center, descriptionPaintFont, descriptionPaint); using var sourcePaint = new SKPaint(); sourcePaint.IsAntialias = true; - sourcePaint.TextSize = 15.0f; sourcePaint.Color = SKColors.White; - sourcePaint.Typeface = fortniteFont; - sourcePaint.TextAlign = SKTextAlign.Right; + using var sourcePaintFont = new SKFont(); + sourcePaintFont.Size = 15.0f; + sourcePaintFont.Typeface = fortniteFont; var fontOffset = lockerItem.SourceType == SourceType.Other ? 10 : 42; - sourcePaint.MeasureText(lockerItem.Source, ref entryNameTextBounds); - canvas.DrawText(lockerItem.Source, bitmap.Width - fontOffset, bitmap.Height - entryNameTextBounds.Height + 8, - sourcePaint); + sourcePaintFont.MeasureText(lockerItem.Source, out entryNameTextBounds, sourcePaint); + canvas.DrawText(lockerItem.Source, bitmap.Width - fontOffset, bitmap.Height - entryNameTextBounds.Height + 8, SKTextAlign.Right, sourcePaintFont, sourcePaint); return bitmap; } @@ -334,15 +330,15 @@ private async Task GenerateFooter(float resizeFactor) using var textPaint = new SKPaint(); textPaint.IsAntialias = true; - textPaint.TextSize = 40.0f * resizeFactor; textPaint.Color = SKColors.White; - textPaint.Typeface = poppinsFont; + using var textPaintFont = new SKFont(); + textPaintFont.Size = 40.0f * resizeFactor; + textPaintFont.Typeface = poppinsFont; //var text = "EasyFnStats.com".ToUpper(); const string text = "EASYFNSTATS.COM"; - var textBounds = new SKRect(); - textPaint.MeasureText(text, ref textBounds); + textPaintFont.MeasureText(text, out var textBounds, textPaint); var imageInfo = new SKImageInfo((int)((50 + 10 + 5 + 10) * resizeFactor + textBounds.Width), (int)(50 * resizeFactor)); @@ -353,8 +349,8 @@ private async Task GenerateFooter(float resizeFactor) using var drawLogoPaint = new SKPaint(); drawLogoPaint.IsAntialias = true; - drawLogoPaint.FilterQuality = SKFilterQuality.High; - canvas.DrawBitmap(logoBitmap, SKRect.Create(0, 0, imageInfo.Height, imageInfo.Height), drawLogoPaint); + canvas.DrawBitmap(logoBitmap, SKRect.Create(0, 0, imageInfo.Height, imageInfo.Height), + new SKSamplingOptions(SKCubicResampler.Mitchell), drawLogoPaint); var splitR = 3 * resizeFactor; @@ -364,7 +360,7 @@ private async Task GenerateFooter(float resizeFactor) canvas.DrawRoundRect((50 + 10) * resizeFactor, (imageInfo.Height - 40 * resizeFactor) / 2, 5 * resizeFactor, 40 * resizeFactor, splitR, splitR, splitPaint); - canvas.DrawText(text, (50 + 10 + 5 + 10) * resizeFactor, (imageInfo.Height + textBounds.Height) / 2, textPaint); + canvas.DrawText(text, (50 + 10 + 5 + 10) * resizeFactor, (imageInfo.Height + textBounds.Height) / 2, SKTextAlign.Left, textPaintFont, textPaint); return bitmap; } diff --git a/Controllers/ShopImageController.cs b/Controllers/ShopImageController.cs index 243c1b0..e8905f4 100644 --- a/Controllers/ShopImageController.cs +++ b/Controllers/ShopImageController.cs @@ -59,13 +59,12 @@ public async Task Shop([FromBody] Shop shop, [FromQuery] bool? fo var templateHash = shop.GetTemplateHash(); var localeTemplateHash = shop.GetLocaleTemplateHash(); - SKBitmap? templateBitmap; SKBitmap templateBitmapCopy; ShopSectionLocationData[]? locationData; using (await namedLock.LockAsync($"shop_template_{templateHash}", cancellationToken).ConfigureAwait(false)) { logger.LogDebug("Acquired shop template lock"); - templateBitmap = cache.Get($"shop_template_bmp_{templateHash}"); + var templateBitmap = cache.Get($"shop_template_bmp_{templateHash}"); locationData = cache.Get($"shop_location_data_{templateHash}"); if (_forceNew || templateBitmap is null) { @@ -115,13 +114,12 @@ public async Task ShopSection( var _isNewShop = isNewShop ?? false; logger.LogInformation("Item Shop section image request received | Locale = {Locale} | New Shop = {SectionId}", locale, section.Id); - SKBitmap? templateBitmap; SKBitmap templateBitmapCopy; ShopSectionLocationData? shopSectionLocationData; using (await namedLock.LockAsync($"shop_section_template_{section.Id}", cancellationToken).ConfigureAwait(false)) { - templateBitmap = cache.Get($"shop_section_template_bmp_{section.Id}"); + var templateBitmap = cache.Get($"shop_section_template_bmp_{section.Id}"); shopSectionLocationData = cache.Get($"shop_section_location_data_{section.Id}"); if (_isNewShop || templateBitmap is null) { @@ -231,25 +229,26 @@ private async Task GenerateShopImage(Shop shop, SKBitmap templateBitma { using var backgroundImagePaint = new SKPaint(); backgroundImagePaint.IsAntialias = true; - backgroundImagePaint.FilterQuality = SKFilterQuality.Medium; canvas.Save(); canvas.ClipRoundRect(new SKRoundRect(imageInfo.Rect, 50), antialias: true); - canvas.DrawBitmap(backgroundBitmap, imageInfo.Rect, backgroundImagePaint); + canvas.DrawBitmap(backgroundBitmap, imageInfo.Rect, + new SKSamplingOptions(SKFilterMode.Linear), backgroundImagePaint); canvas.Restore(); } - canvas.DrawBitmap(templateBitmap, 0, 0); + canvas.DrawBitmap(templateBitmap, 0, 0, SKSamplingOptions.Default); if (shop is { CreatorCode: not null, CreatorCodeTitle: not null }) { using var shopTitlePaint = new SKPaint(); shopTitlePaint.IsAntialias = true; - shopTitlePaint.TextSize = TITLE_FONT_SIZE; - shopTitlePaint.Typeface = await assets.GetFont("Assets/Fonts/Fortnite-86Bold.otf"); + using var shopTitlePaintFont = new SKFont(); + shopTitlePaintFont.Size = TITLE_FONT_SIZE; + shopTitlePaintFont.Typeface = await assets.GetFont("Assets/Fonts/Fortnite-86Bold.otf"); - var shopTitleWidth = shopTitlePaint.MeasureText(shop.Title); + var shopTitleWidth = shopTitlePaintFont.MeasureText(shop.Title, shopTitlePaint); var maxBoxWidth = imageInfo.Width - 3 * HORIZONTAL_PADDING - shopTitleWidth; @@ -257,12 +256,12 @@ private async Task GenerateShopImage(Shop shop, SKBitmap templateBitma { using var creatorCodeBoxBitmap = await GenerateCreatorCodeBox(shop.CreatorCodeTitle, shop.CreatorCode, maxBoxWidth); - canvas.DrawBitmap(creatorCodeBoxBitmap, imageInfo.Width - 100 - creatorCodeBoxBitmap.Width, 100); + canvas.DrawBitmap(creatorCodeBoxBitmap, imageInfo.Width - 100 - creatorCodeBoxBitmap.Width, 100, SKSamplingOptions.Default); } var adBannerBitmap = await assets.GetBitmap("Assets/Images/Shop/ad_banner.png"); // don't dispose canvas.DrawBitmap(adBannerBitmap, imageInfo.Width - 100 - 50 - adBannerBitmap!.Width, - 100 - adBannerBitmap.Height / 2f); + 100 - adBannerBitmap.Height / 2f, SKSamplingOptions.Default); } return bitmap; @@ -275,31 +274,34 @@ private async Task GenerateLocaleTemplate(Shop shop, SKBitmap template var bitmap = new SKBitmap(imageInfo); using var canvas = new SKCanvas(bitmap); - canvas.DrawBitmap(templateBitmap, SKPoint.Empty); + canvas.DrawBitmap(templateBitmap, SKPoint.Empty, SKSamplingOptions.Default); // Drawing the shop title using var shopTitlePaint = new SKPaint(); shopTitlePaint.IsAntialias = true; - shopTitlePaint.TextSize = TITLE_FONT_SIZE; shopTitlePaint.Color = SKColors.White; - shopTitlePaint.Typeface = await assets.GetFont("Assets/Fonts/Fortnite-86Bold.otf"); - var shopTitleWidth = shopTitlePaint.MeasureText(shop.Title); - canvas.DrawText(shop.Title, 100, 50 - shopTitlePaint.FontMetrics.Ascent, shopTitlePaint); + using var shopTitlePaintFont = new SKFont(); + shopTitlePaintFont.Size = TITLE_FONT_SIZE; + shopTitlePaintFont.Typeface = await assets.GetFont("Assets/Fonts/Fortnite-86Bold.otf"); + + var shopTitleWidth = shopTitlePaintFont.MeasureText(shop.Title, shopTitlePaint); + canvas.DrawText(shop.Title, 100, 50 - shopTitlePaintFont.Metrics.Ascent, SKTextAlign.Left, shopTitlePaintFont, shopTitlePaint); // Drawing the date using var datePaint = new SKPaint(); datePaint.IsAntialias = true; - datePaint.TextSize = DATE_FONT_SIZE; datePaint.Color = SKColors.White; - datePaint.Typeface = await assets.GetFont("Assets/Fonts/Fortnite-86BoldItalic.otf"); - datePaint.TextAlign = SKTextAlign.Center; + + using var datePaintFont = new SKFont(); + datePaintFont.Size = DATE_FONT_SIZE; + datePaintFont.Typeface = await assets.GetFont("Assets/Fonts/Fortnite-86BoldItalic.otf"); var datePoint = new SKPoint( Math.Max(HORIZONTAL_PADDING + shopTitleWidth / 2f, - HORIZONTAL_PADDING + datePaint.MeasureText(shop.Date) / 2), - 313 - datePaint.FontMetrics.Ascent); - canvas.DrawText(shop.Date, datePoint, datePaint); + HORIZONTAL_PADDING + datePaintFont.MeasureText(shop.Date, datePaint) / 2), + 313 - datePaintFont.Metrics.Ascent); + canvas.DrawText(shop.Date, datePoint, SKTextAlign.Center, datePaintFont, datePaint); foreach (var sectionLocationData in shopSectionLocationData) { @@ -310,13 +312,14 @@ private async Task GenerateLocaleTemplate(Shop shop, SKBitmap template { using var sectionNamePaint = new SKPaint(); sectionNamePaint.IsAntialias = true; - sectionNamePaint.TextSize = SECTION_NAME_FONT_SIZE; sectionNamePaint.Color = SKColors.White; - sectionNamePaint.Typeface = await assets.GetFont("Assets/Fonts/Fortnite-86BoldItalic.otf"); + using var sectionNamePaintFont = new SKFont(); + sectionNamePaintFont.Size = SECTION_NAME_FONT_SIZE; + sectionNamePaintFont.Typeface = await assets.GetFont("Assets/Fonts/Fortnite-86BoldItalic.otf"); var sectionNamePoint = new SKPoint(sectionLocationData.Name.X, - sectionLocationData.Name.Y - sectionNamePaint.FontMetrics.Ascent); - canvas.DrawText(shopSection?.Name, sectionNamePoint, sectionNamePaint); + sectionLocationData.Name.Y - sectionNamePaintFont.Metrics.Ascent); + canvas.DrawText(shopSection.Name, sectionNamePoint, SKTextAlign.Left, sectionNamePaintFont, sectionNamePaint); } foreach (var entryLocationData in sectionLocationData.Entries) @@ -327,48 +330,52 @@ private async Task GenerateLocaleTemplate(Shop shop, SKBitmap template using var entryNamePaint = new SKPaint(); entryNamePaint.IsAntialias = true; - entryNamePaint.TextSize = ENTRY_NAME_FONT_SIZE; entryNamePaint.Color = SKColors.White; - entryNamePaint.Typeface = await assets.GetFont("Assets/Fonts/Fortnite-75Medium.otf"); + using var entryNamePaintFont = new SKFont(); + entryNamePaintFont.Size = ENTRY_NAME_FONT_SIZE; + entryNamePaintFont.Typeface = await assets.GetFont("Assets/Fonts/Fortnite-75Medium.otf"); - var entryNameTextBounds = new SKRect(); - var nameLines = SplitNameText(shopEntry.Name, entryLocationData.Name.MaxWidth ?? 0, entryNamePaint); + SKRect entryNameTextBounds; + var nameLines = SplitNameText(shopEntry.Name, entryLocationData.Name.MaxWidth ?? 0, + entryNamePaintFont, entryNamePaint); if (nameLines.Length > 1) { - entryNamePaint.MeasureText(nameLines[0], ref entryNameTextBounds); + entryNamePaintFont.MeasureText(nameLines[0], out entryNameTextBounds, entryNamePaint); canvas.DrawText(nameLines[0], entryLocationData.Name.X, - entryLocationData.Name.Y + entryNameTextBounds.Height - 33, entryNamePaint); + entryLocationData.Name.Y + entryNameTextBounds.Height - 33, SKTextAlign.Left, entryNamePaintFont, entryNamePaint); } - entryNamePaint.MeasureText(nameLines.Last(), ref entryNameTextBounds); + entryNamePaintFont.MeasureText(nameLines.Last(), out entryNameTextBounds, entryNamePaint); canvas.DrawText(nameLines.Last(), entryLocationData.Name.X, - entryLocationData.Name.Y + entryNameTextBounds.Height, entryNamePaint); + entryLocationData.Name.Y + entryNameTextBounds.Height, SKTextAlign.Left, entryNamePaintFont, entryNamePaint); // Draw the shop entry price using var pricePaint = new SKPaint(); pricePaint.IsAntialias = true; - pricePaint.TextSize = ENTRY_PRICE_FONT_SIZE; pricePaint.Color = SKColors.White; - pricePaint.Typeface = await assets.GetFont("Assets/Fonts/Fortnite-75Medium.otf"); + using var pricePaintFont = new SKFont(); + pricePaintFont.Size = ENTRY_PRICE_FONT_SIZE; + pricePaintFont.Typeface = await assets.GetFont("Assets/Fonts/Fortnite-75Medium.otf"); - var priceTextWidth = pricePaint.MeasureText(shopEntry.FinalPrice); + var priceTextWidth = pricePaintFont.MeasureText(shopEntry.FinalPrice, pricePaint); var pricePoint = new SKPoint(entryLocationData.Price.X, - entryLocationData.Price.Y - pricePaint.FontMetrics.Descent); - canvas.DrawText(shopEntry.FinalPrice, pricePoint, pricePaint); + entryLocationData.Price.Y - pricePaintFont.Metrics.Descent); + canvas.DrawText(shopEntry.FinalPrice, pricePoint, SKTextAlign.Left, pricePaintFont, pricePaint); // Draw strikeout old price if item is discounted if (shopEntry.FinalPrice != shopEntry.RegularPrice) { using var oldPricePaint = new SKPaint(); oldPricePaint.IsAntialias = true; - oldPricePaint.TextSize = ENTRY_PRICE_FONT_SIZE; oldPricePaint.Color = SKColors.White.WithAlpha((int)(.6 * 255)); - oldPricePaint.Typeface = await assets.GetFont("Assets/Fonts/Fortnite-75Medium.otf"); + using var oldPricePaintFont = new SKFont(); + oldPricePaintFont.Size = ENTRY_PRICE_FONT_SIZE; + oldPricePaintFont.Typeface = await assets.GetFont("Assets/Fonts/Fortnite-75Medium.otf"); - var oldPriceTextWidth = oldPricePaint.MeasureText(shopEntry.RegularPrice); + var oldPriceTextWidth = oldPricePaintFont.MeasureText(shopEntry.RegularPrice, oldPricePaint); var oldPricePoint = new SKPoint(entryLocationData.Price.X + priceTextWidth + 9, - entryLocationData.Price.Y - pricePaint.FontMetrics.Descent); - canvas.DrawText(shopEntry.RegularPrice, oldPricePoint, oldPricePaint); + entryLocationData.Price.Y - pricePaintFont.Metrics.Descent); + canvas.DrawText(shopEntry.RegularPrice, oldPricePoint, SKTextAlign.Left, oldPricePaintFont, oldPricePaint); // Draw the strikeout line using var strikePaint = new SKPaint(); @@ -385,7 +392,7 @@ private async Task GenerateLocaleTemplate(Shop shop, SKBitmap template { using var bannerBitmap = await GenerateBanner(shopEntry.Banner.Text, shopEntry.Banner.Colors, (int)entryLocationData.Banner!.MaxWidth!); - canvas.DrawBitmap(bannerBitmap, entryLocationData.Banner!.X, entryLocationData.Banner.Y); + canvas.DrawBitmap(bannerBitmap, entryLocationData.Banner!.X, entryLocationData.Banner.Y, SKSamplingOptions.Default); } } } @@ -453,7 +460,7 @@ private async Task GenerateLocaleTemplate(Shop shop, SKBitmap template using var itemCardPaint = new SKPaintSafe(); itemCardPaint.IsAntialias = true; itemCardPaint.Shader = SKShader.CreateBitmap(itemCardBitmap, SKShaderTileMode.Clamp, - SKShaderTileMode.Clamp, SKMatrix.CreateTranslation(entryX, entryY)); + SKShaderTileMode.Clamp, SKMatrix.CreateTranslation(entryX, entryY)); sectionCanvas.DrawRoundRect(entryX, entryY, itemCardBitmap.Width, itemCardBitmap.Height, 20, 20, itemCardPaint); @@ -475,7 +482,7 @@ private async Task GenerateLocaleTemplate(Shop shop, SKBitmap template shopLocationData[iSec] = new ShopSectionLocationData(section.Id, sectionNameLocationData, shopEntryData.ToArray()); - canvas.DrawBitmap(sectionBitmap, new SKPoint(sectionX, sectionY)); + canvas.DrawBitmap(sectionBitmap, new SKPoint(sectionX, sectionY), SKSamplingOptions.Default); iSec++; } } @@ -490,25 +497,26 @@ private async Task GenerateCreatorCodeBox(string creatorCodeTitle, str using var creatorCodeTitlePaint = new SKPaint(); creatorCodeTitlePaint.IsAntialias = true; - creatorCodeTitlePaint.TextSize = 100f; - creatorCodeTitlePaint.Typeface = await assets.GetFont("Assets/Fonts/Fortnite-76Bold.otf"); creatorCodeTitlePaint.Color = SKColors.Black; + using var creatorCodeTitlePaintFont = new SKFont(); + creatorCodeTitlePaintFont.Size = 100f; + creatorCodeTitlePaintFont.Typeface = await assets.GetFont("Assets/Fonts/Fortnite-76Bold.otf"); using var creatorCodePaint = new SKPaint(); creatorCodePaint.IsAntialias = true; - creatorCodePaint.TextSize = 100f; - creatorCodePaint.Typeface = await assets.GetFont("Assets/Fonts/Fortnite-76Bold.otf"); creatorCodePaint.Color = new SKColor(178, 165, 255); - creatorCodePaint.TextAlign = SKTextAlign.Right; + using var creatorCodePaintFont = new SKFont(); + creatorCodePaintFont.Size = 100f; + creatorCodePaintFont.Typeface = await assets.GetFont("Assets/Fonts/Fortnite-76Bold.otf"); float width = - creatorCodeTitlePaint.MeasureText(creatorCodeTitle) + creatorCodeTitlePaint.MeasureText(creatorCode), + creatorCodeTitlePaintFont.MeasureText(creatorCodeTitle, creatorCodeTitlePaint) + creatorCodeTitlePaintFont.MeasureText(creatorCode, creatorCodeTitlePaint), height = 150f; while (width > maxWidth) { - creatorCodeTitlePaint.TextSize--; - creatorCodePaint.TextSize--; - width = creatorCodeTitlePaint.MeasureText(creatorCodeTitle) + creatorCodePaint.MeasureText(creatorCode); + creatorCodeTitlePaintFont.Size--; + creatorCodePaintFont.Size--; + width = creatorCodeTitlePaintFont.MeasureText(creatorCodeTitle, creatorCodeTitlePaint) + creatorCodePaintFont.MeasureText(creatorCode, creatorCodePaint); height--; } @@ -522,10 +530,10 @@ private async Task GenerateCreatorCodeBox(string creatorCodeTitle, str boxPaint.Style = SKPaintStyle.Fill; canvas.DrawRoundRect(new SKRect(0, 0, imageInfo.Width, imageInfo.Height), 100, 100, boxPaint); - var y = (imageInfo.Height - creatorCodePaint.FontSpacing) / 2 - creatorCodePaint.FontMetrics.Ascent; + var y = (imageInfo.Height - creatorCodePaintFont.Spacing) / 2 - creatorCodePaintFont.Metrics.Ascent; - canvas.DrawText(creatorCodeTitle, 0, y, creatorCodeTitlePaint); - canvas.DrawText(creatorCode, imageInfo.Width, y, creatorCodePaint); + canvas.DrawText(creatorCodeTitle, 0, y, SKTextAlign.Left, creatorCodeTitlePaintFont, creatorCodeTitlePaint); + canvas.DrawText(creatorCode, imageInfo.Width, y, SKTextAlign.Right, creatorCodePaintFont, creatorCodePaint); return bitmap; } @@ -534,12 +542,12 @@ private async Task GenerateBanner(string text, IReadOnlyList c { using var bannerPaint = new SKPaint(); bannerPaint.IsAntialias = true; - bannerPaint.TextSize = 17.0f; - bannerPaint.Typeface = await assets.GetFont("Assets/Fonts/Fortnite-76BoldItalic.otf"); bannerPaint.Color = SKColor.Parse(colors[1]); + using var bannerPaintFont = new SKFont(); + bannerPaintFont.Size = 17.0f; + bannerPaintFont.Typeface = await assets.GetFont("Assets/Fonts/Fortnite-76BoldItalic.otf"); - var textBounds = new SKRect(); - bannerPaint.MeasureText(text, ref textBounds); + bannerPaintFont.MeasureText(text, out var textBounds, bannerPaint); var maxTextWidth = maxWidth - 2 * 13; var imageInfo = new SKImageInfo(Math.Min(2 * 13 + (int)textBounds.Width, maxWidth), 34); @@ -558,7 +566,7 @@ private async Task GenerateBanner(string text, IReadOnlyList c while (textBounds.Width > maxTextWidth) { text = text.Remove(text.Length - 1, 1); - bannerPaint.MeasureText(text + "...", ref textBounds); + bannerPaintFont.MeasureText(text + "...", out textBounds, bannerPaint); } text += "..."; @@ -566,7 +574,7 @@ private async Task GenerateBanner(string text, IReadOnlyList c // 6 + textBounds.Top - canvas.DrawText(text, 13, (float)imageInfo.Height / 2 - textBounds.MidY, bannerPaint); + canvas.DrawText(text, 13, (float)imageInfo.Height / 2 - textBounds.MidY, SKTextAlign.Left, bannerPaintFont, bannerPaint); return bitmap; } @@ -610,7 +618,7 @@ private async Task GenerateItemCard(ShopEntry shopEntry) new SKPoint(0, imageInfo.Height), [ ImageUtils.ParseColor(shopEntry.BackgroundColors[0]), - ImageUtils.ParseColor(shopEntry.BackgroundColors[2]), // maybe fix this order in payload? + ImageUtils.ParseColor(shopEntry.BackgroundColors[2]), ImageUtils.ParseColor(shopEntry.BackgroundColors[1]), ], [0.0f, 0.5f, 1.0f], @@ -631,10 +639,10 @@ private async Task GenerateItemCard(ShopEntry shopEntry) using var gradientPaint = new SKPaintSafe(); gradientPaint.IsAntialias = true; gradientPaint.Shader = SKShader.CreateRadialGradient( - new SKPoint(imageInfo.Rect.MidX, imageInfo.Rect.MidY), - MathF.Sqrt(MathF.Pow(imageInfo.Rect.MidX, 2) + MathF.Pow(imageInfo.Rect.MidY, 2)), - [new SKColor(129, 207, 250), new SKColor(52, 136, 217)], - SKShaderTileMode.Clamp); + new SKPoint(imageInfo.Rect.MidX, imageInfo.Rect.MidY), + MathF.Sqrt(MathF.Pow(imageInfo.Rect.MidX, 2) + MathF.Pow(imageInfo.Rect.MidY, 2)), + [new SKColor(129, 207, 250), new SKColor(52, 136, 217)], + SKShaderTileMode.Clamp); canvas.DrawRect(0, 0, imageInfo.Width, imageInfo.Height, gradientPaint); } @@ -643,14 +651,12 @@ private async Task GenerateItemCard(ShopEntry shopEntry) if (shopEntry is { ImageType: "track", ImageUrl: null }) { var coverRect = SKRect.Create(10, 10, 236, 236); - using var coverPaint = new SKPaint - { - IsAntialias = true, - FilterQuality = SKFilterQuality.Medium - }; + using var coverPaint = new SKPaint(); + coverPaint.IsAntialias = true; canvas.Save(); canvas.ClipRoundRect(new SKRoundRect(coverRect, 10), antialias: true); - canvas.DrawBitmap(shopEntry.Image, coverRect, coverPaint); + canvas.DrawBitmap(shopEntry.Image, coverRect, + new SKSamplingOptions(SKFilterMode.Linear), coverPaint); canvas.Restore(); } else @@ -669,7 +675,7 @@ private async Task GenerateItemCard(ShopEntry shopEntry) resizeHeight = imageInfo.Height; } - using var imagePaint = new SKPaint { FilterQuality = SKFilterQuality.Medium }; + using var imagePaint = new SKPaint(); // Car bundles get centered in the middle of the card vertically if (shopEntry.ImageType == "car-bundle") @@ -679,7 +685,8 @@ private async Task GenerateItemCard(ShopEntry shopEntry) var sourceRect = new SKRect(0, cropY * sourceScaleY, shopEntry.Image.Width, (cropY + imageInfo.Height) * sourceScaleY); canvas.DrawBitmap(shopEntry.Image, sourceRect, - new SKRect(0, 0, resizeWidth, imageInfo.Height), imagePaint); + new SKRect(0, 0, resizeWidth, imageInfo.Height), + new SKSamplingOptions(SKFilterMode.Linear), imagePaint); } // Center image in the middle of the card, if width is bigger than the image else if (resizeWidth > imageInfo.Width) @@ -689,14 +696,16 @@ private async Task GenerateItemCard(ShopEntry shopEntry) var sourceRect = new SKRect(cropX * sourceScaleX, 0, (cropX + imageInfo.Width) * sourceScaleX, shopEntry.Image.Height); canvas.DrawBitmap(shopEntry.Image, sourceRect, - new SKRect(0, 0, imageInfo.Width, resizeHeight), imagePaint); + new SKRect(0, 0, imageInfo.Width, resizeHeight), + new SKSamplingOptions(SKFilterMode.Linear), imagePaint); } else { var offsetMulti = shopEntry.Size >= 3f ? 0.08f : 0f; canvas.DrawBitmap(shopEntry.Image, new SKRect(0, resizeHeight * -offsetMulti, resizeWidth, - resizeHeight * (1 - offsetMulti)), imagePaint); + resizeHeight * (1 - offsetMulti)), + new SKSamplingOptions(SKFilterMode.Linear), imagePaint); } } @@ -708,14 +717,14 @@ private async Task GenerateItemCard(ShopEntry shopEntry) shadowPaint.IsAntialias = true; shadowPaint.IsDither = true; shadowPaint.Shader = SKShader.CreateLinearGradient( - new SKPoint(imageInfo.Width / 2f, imageInfo.Height), - new SKPoint(imageInfo.Width / 2f, imageInfo.Height * .7f), - [ + new SKPoint(imageInfo.Width / 2f, imageInfo.Height), + new SKPoint(imageInfo.Width / 2f, imageInfo.Height * .7f), + [ textBackgroundColor, textBackgroundColor.WithAlpha(0) - ], - [0.0f, 1.0f], - SKShaderTileMode.Clamp); + ], + [0.0f, 1.0f], + SKShaderTileMode.Clamp); canvas.DrawRect(imageInfo.Rect, shadowPaint); } else if (shopEntry.ImageType == "track") @@ -724,38 +733,38 @@ private async Task GenerateItemCard(ShopEntry shopEntry) shadowPaint.IsAntialias = true; shadowPaint.IsDither = true; shadowPaint.Shader = SKShader.CreateLinearGradient( - new SKPoint(imageInfo.Width / 2f, imageInfo.Height), - new SKPoint(imageInfo.Width / 2f, imageInfo.Height * .6f), - [ + new SKPoint(imageInfo.Width / 2f, imageInfo.Height), + new SKPoint(imageInfo.Width / 2f, imageInfo.Height * .6f), + [ SKColors.Black.WithAlpha((int)(.8 * 255)), SKColors.Black.WithAlpha(0) - ], - [0.0f, 1.0f], - SKShaderTileMode.Clamp); + ], + [0.0f, 1.0f], + SKShaderTileMode.Clamp); canvas.DrawRect(imageInfo.Rect, shadowPaint); } // Draw V-Bucks icon var vbucksBitmap = await assets.GetBitmap("Assets/Images/Shop/vbucks_icon.png"); // don't dispose - canvas.DrawBitmap(vbucksBitmap, 13, imageInfo.Height - vbucksBitmap!.Height - 11); + canvas.DrawBitmap(vbucksBitmap, 13, imageInfo.Height - vbucksBitmap!.Height - 11, SKSamplingOptions.Default); if (shopEntry.IsSpecial) { using var paint = new SKPaint(); - using var font = new SKFont(await assets.GetFont("Assets/Fonts/Fortnite-74Regular.otf"), 35.0f); paint.IsAntialias = true; - paint.TextSize = 35.0f; paint.Color = SKColors.White; - paint.Typeface = await assets.GetFont("Assets/Fonts/Fortnite-74Regular.otf"); - paint.TextAlign = SKTextAlign.Right; + + using var paintFont = new SKFont(); + paintFont.Size = 35.0f; + paintFont.Typeface = await assets.GetFont("Assets/Fonts/Fortnite-74Regular.otf"); - canvas.DrawText("+", imageInfo.Width - 18, imageInfo.Height - paint.FontMetrics.Descent + 3, paint); + canvas.DrawText("+", imageInfo.Width - 18, imageInfo.Height - paintFont.Metrics.Descent + 3, SKTextAlign.Right, paintFont, paint); } return bitmap; } - private static string[] SplitNameText(string text, int maxWidth, SKPaint paint) + private static string[] SplitNameText(string text, int maxWidth, SKFont paintFont, SKPaint paint) { var regex = NameSplitRegex(); var matches = regex.Matches(text); @@ -765,8 +774,7 @@ private static string[] SplitNameText(string text, int maxWidth, SKPaint paint) foreach (Match match in matches) { var line = lines[currentLine]; - var bounds = new SKRect(); - paint.MeasureText(line + match.Value, ref bounds); + paintFont.MeasureText(line + match.Value, out var bounds, paint); if (bounds.Width > maxWidth) currentLine++; if (currentLine >= 2) { @@ -780,14 +788,13 @@ private static string[] SplitNameText(string text, int maxWidth, SKPaint paint) // Adjust lines that are too long and add ellipsis foreach (var line in lines) { - var textBounds = new SKRect(); - paint.MeasureText(line.ToString(), ref textBounds); + paintFont.MeasureText(line.ToString(), out var textBounds, paint); if (textBounds.Width <= maxWidth) continue; while (textBounds.Width > maxWidth) { line.Remove(line.Length - 1, 1); - paint.MeasureText(line + "...", ref textBounds); + paintFont.MeasureText(line + "...", out textBounds, paint); } line.Append("..."); diff --git a/Controllers/StatsImageController.cs b/Controllers/StatsImageController.cs index 1dacf1d..9a86d5f 100644 --- a/Controllers/StatsImageController.cs +++ b/Controllers/StatsImageController.cs @@ -70,10 +70,10 @@ await assets.GetBitmap("data/images/{0}", using var backgroundPaint = new SKPaintSafe(); backgroundPaint.IsAntialias = true; backgroundPaint.Shader = SKShader.CreateRadialGradient( - new SKPoint(imageInfo.Rect.MidX, imageInfo.Rect.MidY), - MathF.Sqrt(MathF.Pow(imageInfo.Rect.MidX, 2) + MathF.Pow(imageInfo.Rect.MidY, 2)), - [new SKColor(41, 165, 224), new SKColor(9, 66, 180)], - SKShaderTileMode.Clamp); + new SKPoint(imageInfo.Rect.MidX, imageInfo.Rect.MidY), + MathF.Sqrt(MathF.Pow(imageInfo.Rect.MidX, 2) + MathF.Pow(imageInfo.Rect.MidY, 2)), + [new SKColor(41, 165, 224), new SKColor(9, 66, 180)], + SKShaderTileMode.Clamp); canvas.DrawRoundRect(0, 0, imageInfo.Width, imageInfo.Height, 50, 50, backgroundPaint); } @@ -81,11 +81,11 @@ await assets.GetBitmap("data/images/{0}", { using var backgroundImagePaint = new SKPaint(); backgroundImagePaint.IsAntialias = true; - backgroundImagePaint.FilterQuality = SKFilterQuality.Medium; canvas.Save(); canvas.ClipRoundRect(new SKRoundRect(imageInfo.Rect, 50), antialias: true); - canvas.DrawBitmap(customBackgroundBitmap, imageInfo.Rect, backgroundImagePaint); + canvas.DrawBitmap(customBackgroundBitmap, imageInfo.Rect, + new SKSamplingOptions(SKFilterMode.Linear), backgroundImagePaint); canvas.Restore(); } @@ -105,22 +105,25 @@ await assets.GetBitmap("data/images/{0}", using var competitiveBoxTitlePaint = new SKPaint(); competitiveBoxTitlePaint.IsAntialias = true; competitiveBoxTitlePaint.Color = SKColors.White; - competitiveBoxTitlePaint.Typeface = fortniteFont; - competitiveBoxTitlePaint.TextSize = 25; + using var competitiveBoxTitlePaintFont = new SKFont(); + competitiveBoxTitlePaintFont.Typeface = fortniteFont; + competitiveBoxTitlePaintFont.Size = 25; using var boxTitlePaint = new SKPaint(); boxTitlePaint.IsAntialias = true; boxTitlePaint.Color = SKColors.White; - boxTitlePaint.Typeface = fortniteFont; - boxTitlePaint.TextSize = 50; + using var boxTitlePaintFont = new SKFont(); + boxTitlePaintFont.Typeface = fortniteFont; + boxTitlePaintFont.Size = 50; using var titlePaint = new SKPaint(); titlePaint.IsAntialias = true; titlePaint.Color = SKColors.LightGray; - titlePaint.Typeface = segoeFont; - titlePaint.TextSize = 20; + using var titlePaintFont = new SKFont(); + titlePaintFont.Typeface = segoeFont; + titlePaintFont.Size = 20; - var textBounds = new SKRect(); + SKRect textBounds; if (type == StatsType.Competitive) { @@ -144,34 +147,34 @@ await assets.GetBitmap("data/images/{0}", canvas.DrawRoundRect(267, 192, 1, 77, 1, 1, splitPaint); var buildLogo = await assets.GetBitmap("Assets/Images/Stats/BuildLogo.png"); // don't dispose - canvas.DrawBitmap(buildLogo, new SKPoint(115, 277)); + canvas.DrawBitmap(buildLogo, new SKPoint(115, 277), SKSamplingOptions.Default); var zeroBuildLogo = await assets.GetBitmap("Assets/Images/Stats/ZeroBuildLogo.png"); // don't dispose - canvas.DrawBitmap(zeroBuildLogo, new SKPoint(317, 277)); + canvas.DrawBitmap(zeroBuildLogo, new SKPoint(317, 277), SKSamplingOptions.Default); - competitiveBoxTitlePaint.MeasureText("OVERALL", ref textBounds); - canvas.DrawText("OVERALL", 211, 305 - textBounds.Top, competitiveBoxTitlePaint); + competitiveBoxTitlePaintFont.MeasureText("OVERALL", out textBounds, competitiveBoxTitlePaint); + canvas.DrawText("OVERALL", 211, 305 - textBounds.Top, SKTextAlign.Left, competitiveBoxTitlePaintFont, competitiveBoxTitlePaint); - titlePaint.MeasureText("Earnings", ref textBounds); - canvas.DrawText("Earnings", 70, 338 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("Earnings", out textBounds, titlePaint); + canvas.DrawText("Earnings", 70, 338 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("Power Ranking", ref textBounds); - canvas.DrawText("Power Ranking", 250, 338 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("Power Ranking", out textBounds, titlePaint); + canvas.DrawText("Power Ranking", 250, 338 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("Games", ref textBounds); - canvas.DrawText("Games", 70, 414 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("Games", out textBounds, titlePaint); + canvas.DrawText("Games", 70, 414 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("Wins", ref textBounds); - canvas.DrawText("Wins", 231, 414 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("Wins", out textBounds, titlePaint); + canvas.DrawText("Wins", 231, 414 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("Win%", ref textBounds); - canvas.DrawText("Win%", 370, 414 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("Win%", out textBounds, titlePaint); + canvas.DrawText("Win%", 370, 414 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("Kills", ref textBounds); - canvas.DrawText("Kills", 70, 491 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("Kills", out textBounds, titlePaint); + canvas.DrawText("Kills", 70, 491 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("K/D", ref textBounds); - canvas.DrawText("K/D", 231, 491 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("K/D", out textBounds, titlePaint); + canvas.DrawText("K/D", 231, 491 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); } else { @@ -179,38 +182,38 @@ await assets.GetBitmap("data/images/{0}", DrawBlurredRoundRect(bitmap, overallBoxRect); canvas.DrawRoundRect(overallBoxRect, boxPaint); - boxTitlePaint.MeasureText("OVERALL", ref textBounds); - canvas.DrawText("OVERALL", 60, 134 - textBounds.Top, boxTitlePaint); + boxTitlePaintFont.MeasureText("OVERALL", out textBounds, boxTitlePaint); + canvas.DrawText("OVERALL", 60, 134 - textBounds.Top, SKTextAlign.Left, boxTitlePaintFont, boxTitlePaint); - titlePaint.MeasureText("Games", ref textBounds); - canvas.DrawText("Games", 70, 184 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("Games", out textBounds, titlePaint); + canvas.DrawText("Games", 70, 184 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("Wins", ref textBounds); - canvas.DrawText("Wins", 231, 184 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("Wins", out textBounds, titlePaint); + canvas.DrawText("Wins", 231, 184 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("Win%", ref textBounds); - canvas.DrawText("Win%", 370, 184 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("Win%", out textBounds, titlePaint); + canvas.DrawText("Win%", 370, 184 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("Kills", ref textBounds); - canvas.DrawText("Kills", 70, 261 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("Kills", out textBounds, titlePaint); + canvas.DrawText("Kills", 70, 261 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("K/D", ref textBounds); - canvas.DrawText("K/D", 231, 261 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("K/D", out textBounds, titlePaint); + canvas.DrawText("K/D", 231, 261 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("Playtime since Season 7", ref textBounds); - canvas.DrawText("Playtime since Season 7", 70, 338 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("Playtime since Season 7", out textBounds, titlePaint); + canvas.DrawText("Playtime since Season 7", 70, 338 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("days", ref textBounds); - canvas.DrawText("days", 70, 397 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("days", out textBounds, titlePaint); + canvas.DrawText("days", 70, 397 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("hours", ref textBounds); - canvas.DrawText("hours", 147, 397 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("hours", out textBounds, titlePaint); + canvas.DrawText("hours", 147, 397 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("minutes", ref textBounds); - canvas.DrawText("minutes", 231, 397 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("minutes", out textBounds, titlePaint); + canvas.DrawText("minutes", 231, 397 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("BattlePass Level", ref textBounds); - canvas.DrawText("BattlePass Level", 70, 442 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("BattlePass Level", out textBounds, titlePaint); + canvas.DrawText("BattlePass Level", 70, 442 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); using var battlePassBarBackgroundPaint = new SKPaint(); battlePassBarBackgroundPaint.IsAntialias = true; @@ -223,116 +226,116 @@ await assets.GetBitmap("data/images/{0}", DrawBlurredRoundRect(bitmap, soloBoxRect); canvas.DrawRoundRect(soloBoxRect, boxPaint); - boxTitlePaint.MeasureText("SOLO", ref textBounds); - canvas.DrawText("SOLO", 527, 134 - textBounds.Top, boxTitlePaint); + boxTitlePaintFont.MeasureText("SOLO", out textBounds, boxTitlePaint); + canvas.DrawText("SOLO", 527, 134 - textBounds.Top, SKTextAlign.Left, boxTitlePaintFont, boxTitlePaint); var soloIcon = await assets.GetBitmap("Assets/Images/Stats/PlaylistIcons/solo.png"); // don't dispose - canvas.DrawBitmap(soloIcon, new SKPoint(648, 134)); + canvas.DrawBitmap(soloIcon, new SKPoint(648, 134), SKSamplingOptions.Default); - titlePaint.MeasureText("Games", ref textBounds); - canvas.DrawText("Games", 537, 184 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("Games", out textBounds, titlePaint); + canvas.DrawText("Games", 537, 184 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("Wins", ref textBounds); - canvas.DrawText("Wins", 698, 184 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("Wins", out textBounds, titlePaint); + canvas.DrawText("Wins", 698, 184 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("Win%", ref textBounds); - canvas.DrawText("Win%", 837, 184 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("Win%", out textBounds, titlePaint); + canvas.DrawText("Win%", 837, 184 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("Kills", ref textBounds); - canvas.DrawText("Kills", 537, 261 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("Kills", out textBounds, titlePaint); + canvas.DrawText("Kills", 537, 261 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("K/D", ref textBounds); - canvas.DrawText("K/D", 698, 261 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("K/D", out textBounds, titlePaint); + canvas.DrawText("K/D", 698, 261 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("Top 25", ref textBounds); - canvas.DrawText("Top 25", 837, 261 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("Top 25", out textBounds, titlePaint); + canvas.DrawText("Top 25", 837, 261 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); // Duos var duosBoxRect = new SKRoundRect(SKRect.Create(996, 159, 459, 185), 30); DrawBlurredRoundRect(bitmap, duosBoxRect); canvas.DrawRoundRect(duosBoxRect, boxPaint); - boxTitlePaint.MeasureText("DUOS", ref textBounds); - canvas.DrawText("DUOS", 1006, 134 - textBounds.Top, boxTitlePaint); + boxTitlePaintFont.MeasureText("DUOS", out textBounds, boxTitlePaint); + canvas.DrawText("DUOS", 1006, 134 - textBounds.Top, SKTextAlign.Left, boxTitlePaintFont, boxTitlePaint); var duosIcon = await assets.GetBitmap("Assets/Images/Stats/PlaylistIcons/duos.png"); // don't dispose - canvas.DrawBitmap(duosIcon, new SKPoint(1133, 134)); + canvas.DrawBitmap(duosIcon, new SKPoint(1133, 134), SKSamplingOptions.Default); - titlePaint.MeasureText("Games", ref textBounds); - canvas.DrawText("Games", 1016, 184 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("Games", out textBounds, titlePaint); + canvas.DrawText("Games", 1016, 184 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("Wins", ref textBounds); - canvas.DrawText("Wins", 1177, 184 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("Wins", out textBounds, titlePaint); + canvas.DrawText("Wins", 1177, 184 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("Win%", ref textBounds); - canvas.DrawText("Win%", 1316, 184 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("Win%", out textBounds, titlePaint); + canvas.DrawText("Win%", 1316, 184 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("Kills", ref textBounds); - canvas.DrawText("Kills", 1016, 261 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("Kills", out textBounds, titlePaint); + canvas.DrawText("Kills", 1016, 261 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("K/D", ref textBounds); - canvas.DrawText("K/D", 1177, 261 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("K/D", out textBounds, titlePaint); + canvas.DrawText("K/D", 1177, 261 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("Top 12", ref textBounds); - canvas.DrawText("Top 12", 1316, 261 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("Top 12", out textBounds, titlePaint); + canvas.DrawText("Top 12", 1316, 261 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); // Trios var triosBoxRect = new SKRoundRect(SKRect.Create(517, 389, 459, 185), 30); DrawBlurredRoundRect(bitmap, triosBoxRect); canvas.DrawRoundRect(triosBoxRect, boxPaint); - boxTitlePaint.MeasureText("TRIOS", ref textBounds); - canvas.DrawText("TRIOS", 527, 364 - textBounds.Top, boxTitlePaint); + boxTitlePaintFont.MeasureText("TRIOS", out textBounds, boxTitlePaint); + canvas.DrawText("TRIOS", 527, 364 - textBounds.Top, SKTextAlign.Left, boxTitlePaintFont, boxTitlePaint); var triosIcon = await assets.GetBitmap(@"Assets/Images/Stats/PlaylistIcons/trios.png"); // don't dispose - canvas.DrawBitmap(triosIcon, new SKPoint(663, 364)); + canvas.DrawBitmap(triosIcon, new SKPoint(663, 364), SKSamplingOptions.Default); - titlePaint.MeasureText("Games", ref textBounds); - canvas.DrawText("Games", 537, 414 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("Games", out textBounds, titlePaint); + canvas.DrawText("Games", 537, 414 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("Wins", ref textBounds); - canvas.DrawText("Wins", 698, 414 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("Wins", out textBounds, titlePaint); + canvas.DrawText("Wins", 698, 414 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("Win%", ref textBounds); - canvas.DrawText("Win%", 837, 414 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("Win%", out textBounds, titlePaint); + canvas.DrawText("Win%", 837, 414 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("Kills", ref textBounds); - canvas.DrawText("Kills", 537, 491 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("Kills", out textBounds, titlePaint); + canvas.DrawText("Kills", 537, 491 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("K/D", ref textBounds); - canvas.DrawText("K/D", 698, 491 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("K/D", out textBounds, titlePaint); + canvas.DrawText("K/D", 698, 491 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("Top 6", ref textBounds); - canvas.DrawText("Top 6", 837, 491 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("Top 6", out textBounds, titlePaint); + canvas.DrawText("Top 6", 837, 491 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); // Squads var squadsBoxRect = new SKRoundRect(SKRect.Create(996, 389, 459, 185), 30); DrawBlurredRoundRect(bitmap, squadsBoxRect); canvas.DrawRoundRect(squadsBoxRect, boxPaint); - boxTitlePaint.MeasureText("SQUADS", ref textBounds); - canvas.DrawText("SQUADS", 1006, 364 - textBounds.Top, boxTitlePaint); + boxTitlePaintFont.MeasureText("SQUADS", out textBounds, boxTitlePaint); + canvas.DrawText("SQUADS", 1006, 364 - textBounds.Top, SKTextAlign.Left, boxTitlePaintFont, boxTitlePaint); var squadsIcon = await assets.GetBitmap(@"Assets/Images/Stats/PlaylistIcons/squads.png"); // don't dispose - canvas.DrawBitmap(squadsIcon, new SKPoint(1191, 364)); + canvas.DrawBitmap(squadsIcon, new SKPoint(1191, 364), SKSamplingOptions.Default); - titlePaint.MeasureText("Games", ref textBounds); - canvas.DrawText("Games", 1016, 414 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("Games", out textBounds, titlePaint); + canvas.DrawText("Games", 1016, 414 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("Wins", ref textBounds); - canvas.DrawText("Wins", 1177, 414 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("Wins", out textBounds, titlePaint); + canvas.DrawText("Wins", 1177, 414 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("Win%", ref textBounds); - canvas.DrawText("Win%", 1316, 414 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("Win%", out textBounds, titlePaint); + canvas.DrawText("Win%", 1316, 414 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("Kills", ref textBounds); - canvas.DrawText("Kills", 1016, 491 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("Kills", out textBounds, titlePaint); + canvas.DrawText("Kills", 1016, 491 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("K/D", ref textBounds); - canvas.DrawText("K/D", 1177, 491 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("K/D", out textBounds, titlePaint); + canvas.DrawText("K/D", 1177, 491 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("Top 6", ref textBounds); - canvas.DrawText("Top 6", 1316, 491 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("Top 6", out textBounds, titlePaint); + canvas.DrawText("Top 6", 1316, 491 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); if (type == StatsType.Normal) { @@ -341,26 +344,26 @@ await assets.GetBitmap("data/images/{0}", DrawBlurredRoundRect(bitmap, teamsBoxRect); canvas.DrawRoundRect(teamsBoxRect, boxPaint); - boxTitlePaint.MeasureText("TEAMS", ref textBounds); - canvas.DrawText("TEAMS", 527, 594 - textBounds.Top, boxTitlePaint); + boxTitlePaintFont.MeasureText("TEAMS", out textBounds, boxTitlePaint); + canvas.DrawText("TEAMS", 527, 594 - textBounds.Top, SKTextAlign.Left, boxTitlePaintFont, boxTitlePaint); var teamsIcon = await assets.GetBitmap("Assets/Images/Stats/PlaylistIcons/teams.png"); // don't dispose - canvas.DrawBitmap(teamsIcon, new SKPoint(683, 594)); + canvas.DrawBitmap(teamsIcon, new SKPoint(683, 594), SKSamplingOptions.Default); - titlePaint.MeasureText("Games", ref textBounds); - canvas.DrawText("Games", 537, 644 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("Games", out textBounds, titlePaint); + canvas.DrawText("Games", 537, 644 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("Wins", ref textBounds); - canvas.DrawText("Wins", 698, 644 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("Wins", out textBounds, titlePaint); + canvas.DrawText("Wins", 698, 644 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("Win%", ref textBounds); - canvas.DrawText("Win%", 837, 644 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("Win%", out textBounds, titlePaint); + canvas.DrawText("Win%", 837, 644 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("Kills", ref textBounds); - canvas.DrawText("Kills", 954, 644 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("Kills", out textBounds, titlePaint); + canvas.DrawText("Kills", 954, 644 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); - titlePaint.MeasureText("K/D", ref textBounds); - canvas.DrawText("K/D", 1115, 644 - textBounds.Top, titlePaint); + titlePaintFont.MeasureText("K/D", out textBounds, titlePaint); + canvas.DrawText("K/D", 1115, 644 - textBounds.Top, SKTextAlign.Left, titlePaintFont, titlePaint); } return bitmap; @@ -372,7 +375,7 @@ private async Task GenerateImage(Stats stats, StatsType type, SKBitmap var bitmap = new SKBitmap(imageInfo); using var canvas = new SKCanvas(bitmap); - canvas.DrawBitmap(templateBitmap, SKPoint.Empty); + canvas.DrawBitmap(templateBitmap, SKPoint.Empty, SKSamplingOptions.Default); var fortniteFont = await assets.GetFont("Assets/Fonts/Fortnite.ttf"); // don't dispose var segoeFont = await assets.GetFont("Assets/Fonts/Segoe.ttf"); // don't dispose @@ -380,61 +383,60 @@ private async Task GenerateImage(Stats stats, StatsType type, SKBitmap using var namePaint = new SKPaint(); namePaint.IsAntialias = true; namePaint.Color = SKColors.White; - namePaint.Typeface = segoeFont; - namePaint.TextSize = 64; - namePaint.FilterQuality = SKFilterQuality.Medium; + using var namePaintFont = new SKFont(); + namePaintFont.Typeface = segoeFont; + namePaintFont.Size = 64; using var titlePaint = new SKPaint(); titlePaint.IsAntialias = true; titlePaint.Color = SKColors.LightGray; - titlePaint.Typeface = segoeFont; - titlePaint.TextSize = 20; - titlePaint.FilterQuality = SKFilterQuality.Medium; + using var titlePaintFont = new SKFont(); + titlePaintFont.Typeface = segoeFont; + titlePaintFont.Size = 20; using var valuePaint = new SKPaint(); valuePaint.IsAntialias = true; valuePaint.Color = SKColors.White; - valuePaint.Typeface = fortniteFont; - valuePaint.TextSize = 35; - valuePaint.FilterQuality = SKFilterQuality.Medium; + using var valuePaintFont = new SKFont(); + valuePaintFont.Typeface = fortniteFont; + valuePaintFont.Size = 35; using var divisionPaint = new SKPaint(); divisionPaint.IsAntialias = true; divisionPaint.Color = SKColors.White; - divisionPaint.Typeface = fortniteFont; - divisionPaint.TextSize = 35; - divisionPaint.FilterQuality = SKFilterQuality.Medium; + using var divisionPaintFont = new SKFont(); + divisionPaintFont.Typeface = fortniteFont; + divisionPaintFont.Size = 35; using var rankProgressPaint = new SKPaint(); rankProgressPaint.IsAntialias = true; rankProgressPaint.Color = SKColors.White.WithAlpha((int)(255 * 0.7)); - rankProgressPaint.Typeface = segoeFont; - rankProgressPaint.TextSize = 16; - rankProgressPaint.FilterQuality = SKFilterQuality.Medium; + using var rankProgressPaintFont = new SKFont(); + rankProgressPaintFont.Typeface = segoeFont; + rankProgressPaintFont.Size = 16; using var rankingPaint = new SKPaint(); rankingPaint.IsAntialias = true; rankingPaint.Color = SKColors.White; - rankingPaint.Typeface = fortniteFont; - rankingPaint.TextSize = 20; - rankingPaint.FilterQuality = SKFilterQuality.Medium; - - var textBounds = new SKRect(); + using var rankingPaintFont = new SKFont(); + rankingPaintFont.Typeface = fortniteFont; + rankingPaintFont.Size = 20; var inputIcon = await assets.GetBitmap($"Assets/Images/Stats/InputTypes/{stats.InputType}.png"); // don't dispose - canvas.DrawBitmap(inputIcon, 50, 50); + canvas.DrawBitmap(inputIcon, 50, 50, SKSamplingOptions.Default); - namePaint.MeasureText(stats.PlayerName, ref textBounds); - canvas.DrawText(stats.PlayerName, 159, 58 - textBounds.Top, namePaint); + namePaintFont.MeasureText(stats.PlayerName, out var textBounds, namePaint); + canvas.DrawText(stats.PlayerName, 159, 58 - textBounds.Top, + SKTextAlign.Left, namePaintFont, namePaint); if (stats.IsVerified) { var verifiedIcon = await assets.GetBitmap("Assets/Images/Stats/Verified.png"); // don't dispose - canvas.DrawBitmap(verifiedIcon, 159 + textBounds.Width + 5, 47); + canvas.DrawBitmap(verifiedIcon, 159 + textBounds.Width + 5, 47, SKSamplingOptions.Default); using var discordBoxBitmap = await ImageUtils.GenerateDiscordBox(assets, stats.UserName ?? "???#0000"); - canvas.DrawBitmap(discordBoxBitmap, imageInfo.Width - 50 - discordBoxBitmap.Width, 39); + canvas.DrawBitmap(discordBoxBitmap, imageInfo.Width - 50 - discordBoxBitmap.Width, 39, SKSamplingOptions.Default); } if (type == StatsType.Competitive) @@ -453,17 +455,17 @@ private async Task GenerateImage(Stats stats, StatsType type, SKBitmap var divisionIconBitmap = await assets.GetBitmap( $"Assets/Images/Stats/DivisionIcons/{divisionAssetName}.png"); // don't dispose - canvas.DrawBitmap(divisionIconBitmap, x - divisionIconBitmap!.Width / 2f, 109); + canvas.DrawBitmap(divisionIconBitmap, x - divisionIconBitmap!.Width / 2f, 109, SKSamplingOptions.Default); - divisionPaint.MeasureText(rankedStatsEntry.CurrentDivisionName, ref textBounds); + divisionPaintFont.MeasureText(rankedStatsEntry.CurrentDivisionName, out textBounds, divisionPaint); canvas.DrawText(rankedStatsEntry.CurrentDivisionName, x - (int)(textBounds.Width / 2), - 206 - textBounds.Top, divisionPaint); + 206 - textBounds.Top, SKTextAlign.Left, divisionPaintFont, divisionPaint); if (rankedStatsEntry.Ranking is null) { const int maxBarWidth = 130, barHeight = 6; var progressText = $"{(int)(rankedStatsEntry.Progress * 100)}%"; - rankProgressPaint.MeasureText(progressText, ref textBounds); + rankProgressPaintFont.MeasureText(progressText, out textBounds, rankProgressPaint); var barX = x - textBounds.Width / 2f - maxBarWidth / 2f; using var barBackgroundPaint = new SKPaint(); @@ -478,77 +480,76 @@ await assets.GetBitmap( using var battlePassBarPaint = new SKPaintSafe(); battlePassBarPaint.IsAntialias = true; battlePassBarPaint.Shader = SKShader.CreateLinearGradient( - new SKPoint(barX, 0), - new SKPoint(barX + rankProgressBarWidth, 0), - [ + new SKPoint(barX, 0), + new SKPoint(barX + rankProgressBarWidth, 0), + [ SKColor.Parse(stats.BattlePassLevelBarColors[0]), SKColor.Parse(stats.BattlePassLevelBarColors[1]) - ], - [0, 1], - SKShaderTileMode.Repeat); + ], + [0, 1], + SKShaderTileMode.Repeat); canvas.DrawRoundRect(barX, 250, rankProgressBarWidth, barHeight, 10, 10, battlePassBarPaint); } - canvas.DrawText(progressText, barX + maxBarWidth + 7, 247 - textBounds.Top, rankProgressPaint); + canvas.DrawText(progressText, barX + maxBarWidth + 7, 247 - textBounds.Top, SKTextAlign.Left, rankProgressPaintFont, rankProgressPaint); } else { - rankingPaint.MeasureText(rankedStatsEntry.Ranking, ref textBounds); - canvas.DrawText(rankedStatsEntry.Ranking, x - (int)(textBounds.Width / 2), 245 - textBounds.Top, - rankingPaint); + rankingPaintFont.MeasureText(rankedStatsEntry.Ranking, out textBounds, rankingPaint); + canvas.DrawText(rankedStatsEntry.Ranking, x - (int)(textBounds.Width / 2), 245 - textBounds.Top, SKTextAlign.Left, rankingPaintFont, rankingPaint); } } - valuePaint.MeasureText(stats.Competitive.Earnings, ref textBounds); - canvas.DrawText(stats.Competitive.Earnings, 70, 365 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Competitive.Earnings, out textBounds, valuePaint); + canvas.DrawText(stats.Competitive.Earnings, 70, 365 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Competitive.PowerRanking, ref textBounds); - canvas.DrawText(stats.Competitive.PowerRanking, 250, 365 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Competitive.PowerRanking, out textBounds, valuePaint); + canvas.DrawText(stats.Competitive.PowerRanking, 250, 365 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Overall.MatchesPlayed, ref textBounds); - canvas.DrawText(stats.Overall.MatchesPlayed, 70, 441 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Overall.MatchesPlayed, out textBounds, valuePaint); + canvas.DrawText(stats.Overall.MatchesPlayed, 70, 441 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Overall.Wins, ref textBounds); - canvas.DrawText(stats.Overall.Wins, 231, 441 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Overall.Wins, out textBounds, valuePaint); + canvas.DrawText(stats.Overall.Wins, 231, 441 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Overall.WinRatio, ref textBounds); - canvas.DrawText(stats.Overall.WinRatio, 370, 441 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Overall.WinRatio, out textBounds, valuePaint); + canvas.DrawText(stats.Overall.WinRatio, 370, 441 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Overall.Kills, ref textBounds); - canvas.DrawText(stats.Overall.Kills, 70, 518 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Overall.Kills, out textBounds, valuePaint); + canvas.DrawText(stats.Overall.Kills, 70, 518 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Overall.KD, ref textBounds); - canvas.DrawText(stats.Overall.KD, 231, 518 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Overall.KD, out textBounds, valuePaint); + canvas.DrawText(stats.Overall.KD, 231, 518 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); } else { - valuePaint.MeasureText(stats.Overall.MatchesPlayed, ref textBounds); - canvas.DrawText(stats.Overall.MatchesPlayed, 70, 211 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Overall.MatchesPlayed, out textBounds, valuePaint); + canvas.DrawText(stats.Overall.MatchesPlayed, 70, 211 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Overall.Wins, ref textBounds); - canvas.DrawText(stats.Overall.Wins, 231, 211 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Overall.Wins, out textBounds, valuePaint); + canvas.DrawText(stats.Overall.Wins, 231, 211 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Overall.WinRatio, ref textBounds); - canvas.DrawText(stats.Overall.WinRatio, 370, 211 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Overall.WinRatio, out textBounds, valuePaint); + canvas.DrawText(stats.Overall.WinRatio, 370, 211 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Overall.Kills, ref textBounds); - canvas.DrawText(stats.Overall.Kills, 70, 288 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Overall.Kills, out textBounds, valuePaint); + canvas.DrawText(stats.Overall.Kills, 70, 288 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Overall.KD, ref textBounds); - canvas.DrawText(stats.Overall.KD, 231, 288 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Overall.KD, out textBounds, valuePaint); + canvas.DrawText(stats.Overall.KD, 231, 288 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Playtime.Days, ref textBounds); - canvas.DrawText(stats.Playtime.Days, 70, 369 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Playtime.Days, out textBounds, valuePaint); + canvas.DrawText(stats.Playtime.Days, 70, 369 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Playtime.Hours, ref textBounds); - canvas.DrawText(stats.Playtime.Hours, 147, 369 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Playtime.Hours, out textBounds, valuePaint); + canvas.DrawText(stats.Playtime.Hours, 147, 369 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Playtime.Minutes, ref textBounds); - canvas.DrawText(stats.Playtime.Minutes, 213, 369 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Playtime.Minutes, out textBounds, valuePaint); + canvas.DrawText(stats.Playtime.Minutes, 213, 369 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); var battlePassLevel = ((int)stats.BattlePassLevel).ToString(); - valuePaint.MeasureText(battlePassLevel, ref textBounds); - canvas.DrawText(battlePassLevel, 70, 479 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(battlePassLevel, out textBounds, valuePaint); + canvas.DrawText(battlePassLevel, 70, 479 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); const int maxBarWidth = 309, barHeight = 20; @@ -559,110 +560,110 @@ await assets.GetBitmap( using var battlePassBarPaint = new SKPaintSafe(); battlePassBarPaint.IsAntialias = true; battlePassBarPaint.Shader = SKShader.CreateLinearGradient( - new SKPoint(158, 0), - new SKPoint(158 + battlePassBarWidth, 0), - [ + new SKPoint(158, 0), + new SKPoint(158 + battlePassBarWidth, 0), + [ SKColor.Parse(stats.BattlePassLevelBarColors[0]), SKColor.Parse(stats.BattlePassLevelBarColors[1]) - ], - [0, 1], - SKShaderTileMode.Repeat); + ], + [0, 1], + SKShaderTileMode.Repeat); canvas.DrawRoundRect(158, 483, battlePassBarWidth, barHeight, 10, 10, battlePassBarPaint); } } - valuePaint.MeasureText(stats.Solo.MatchesPlayed, ref textBounds); - canvas.DrawText(stats.Solo.MatchesPlayed, 537, 211 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Solo.MatchesPlayed, out textBounds, valuePaint); + canvas.DrawText(stats.Solo.MatchesPlayed, 537, 211 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Solo.Wins, ref textBounds); - canvas.DrawText(stats.Solo.Wins, 698, 211 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Solo.Wins, out textBounds, valuePaint); + canvas.DrawText(stats.Solo.Wins, 698, 211 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Solo.WinRatio, ref textBounds); - canvas.DrawText(stats.Solo.WinRatio, 837, 211 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Solo.WinRatio, out textBounds, valuePaint); + canvas.DrawText(stats.Solo.WinRatio, 837, 211 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Solo.Kills, ref textBounds); - canvas.DrawText(stats.Solo.Kills, 537, 288 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Solo.Kills, out textBounds, valuePaint); + canvas.DrawText(stats.Solo.Kills, 537, 288 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Solo.KD, ref textBounds); - canvas.DrawText(stats.Solo.KD, 698, 288 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Solo.KD, out textBounds, valuePaint); + canvas.DrawText(stats.Solo.KD, 698, 288 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Solo.Top25, ref textBounds); - canvas.DrawText(stats.Solo.Top25, 837, 288 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Solo.Top25, out textBounds, valuePaint); + canvas.DrawText(stats.Solo.Top25, 837, 288 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Duos.MatchesPlayed, ref textBounds); - canvas.DrawText(stats.Duos.MatchesPlayed, 1016, 211 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Duos.MatchesPlayed, out textBounds, valuePaint); + canvas.DrawText(stats.Duos.MatchesPlayed, 1016, 211 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Duos.Wins, ref textBounds); - canvas.DrawText(stats.Duos.Wins, 1177, 211 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Duos.Wins, out textBounds, valuePaint); + canvas.DrawText(stats.Duos.Wins, 1177, 211 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Duos.WinRatio, ref textBounds); - canvas.DrawText(stats.Duos.WinRatio, 1316, 211 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Duos.WinRatio, out textBounds, valuePaint); + canvas.DrawText(stats.Duos.WinRatio, 1316, 211 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Duos.Kills, ref textBounds); - canvas.DrawText(stats.Duos.Kills, 1016, 288 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Duos.Kills, out textBounds, valuePaint); + canvas.DrawText(stats.Duos.Kills, 1016, 288 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Duos.KD, ref textBounds); - canvas.DrawText(stats.Duos.KD, 1177, 288 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Duos.KD, out textBounds, valuePaint); + canvas.DrawText(stats.Duos.KD, 1177, 288 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Duos.Top12, ref textBounds); - canvas.DrawText(stats.Duos.Top12, 1316, 288 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Duos.Top12, out textBounds, valuePaint); + canvas.DrawText(stats.Duos.Top12, 1316, 288 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Trios.MatchesPlayed, ref textBounds); - canvas.DrawText(stats.Trios.MatchesPlayed, 537, 441 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Trios.MatchesPlayed, out textBounds, valuePaint); + canvas.DrawText(stats.Trios.MatchesPlayed, 537, 441 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Trios.Wins, ref textBounds); - canvas.DrawText(stats.Trios.Wins, 698, 441 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Trios.Wins, out textBounds, valuePaint); + canvas.DrawText(stats.Trios.Wins, 698, 441 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Trios.WinRatio, ref textBounds); - canvas.DrawText(stats.Trios.WinRatio, 837, 441 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Trios.WinRatio, out textBounds, valuePaint); + canvas.DrawText(stats.Trios.WinRatio, 837, 441 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Trios.Kills, ref textBounds); - canvas.DrawText(stats.Trios.Kills, 537, 518 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Trios.Kills, out textBounds, valuePaint); + canvas.DrawText(stats.Trios.Kills, 537, 518 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Trios.KD, ref textBounds); - canvas.DrawText(stats.Trios.KD, 698, 518 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Trios.KD, out textBounds, valuePaint); + canvas.DrawText(stats.Trios.KD, 698, 518 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Trios.Top6, ref textBounds); - canvas.DrawText(stats.Trios.Top6, 837, 518 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Trios.Top6, out textBounds, valuePaint); + canvas.DrawText(stats.Trios.Top6, 837, 518 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Squads.MatchesPlayed, ref textBounds); - canvas.DrawText(stats.Squads.MatchesPlayed, 1016, 441 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Squads.MatchesPlayed, out textBounds, valuePaint); + canvas.DrawText(stats.Squads.MatchesPlayed, 1016, 441 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Squads.Wins, ref textBounds); - canvas.DrawText(stats.Squads.Wins, 1177, 441 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Squads.Wins, out textBounds, valuePaint); + canvas.DrawText(stats.Squads.Wins, 1177, 441 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Squads.WinRatio, ref textBounds); - canvas.DrawText(stats.Squads.WinRatio, 1316, 441 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Squads.WinRatio, out textBounds, valuePaint); + canvas.DrawText(stats.Squads.WinRatio, 1316, 441 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Squads.Kills, ref textBounds); - canvas.DrawText(stats.Squads.Kills, 1016, 518 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Squads.Kills, out textBounds, valuePaint); + canvas.DrawText(stats.Squads.Kills, 1016, 518 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Squads.KD, ref textBounds); - canvas.DrawText(stats.Squads.KD, 1177, 518 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Squads.KD, out textBounds, valuePaint); + canvas.DrawText(stats.Squads.KD, 1177, 518 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Squads.Top6, ref textBounds); - canvas.DrawText(stats.Squads.Top6, 1316, 518 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Squads.Top6, out textBounds, valuePaint); + canvas.DrawText(stats.Squads.Top6, 1316, 518 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); if (type == StatsType.Normal && stats.Teams is not null) { - valuePaint.MeasureText(stats.Teams.MatchesPlayed, ref textBounds); - canvas.DrawText(stats.Teams.MatchesPlayed, 537, 671 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Teams.MatchesPlayed, out textBounds, valuePaint); + canvas.DrawText(stats.Teams.MatchesPlayed, 537, 671 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Teams.Wins, ref textBounds); - canvas.DrawText(stats.Teams.Wins, 698, 671 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Teams.Wins, out textBounds, valuePaint); + canvas.DrawText(stats.Teams.Wins, 698, 671 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Teams.WinRatio, ref textBounds); - canvas.DrawText(stats.Teams.WinRatio, 837, 671 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Teams.WinRatio, out textBounds, valuePaint); + canvas.DrawText(stats.Teams.WinRatio, 837, 671 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Teams.Kills, ref textBounds); - canvas.DrawText(stats.Teams.Kills, 954, 671 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Teams.Kills, out textBounds, valuePaint); + canvas.DrawText(stats.Teams.Kills, 954, 671 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); - valuePaint.MeasureText(stats.Teams.KD, ref textBounds); - canvas.DrawText(stats.Teams.KD, 1115, 671 - textBounds.Top, valuePaint); + valuePaintFont.MeasureText(stats.Teams.KD, out textBounds, valuePaint); + canvas.DrawText(stats.Teams.KD, 1115, 671 - textBounds.Top, SKTextAlign.Left, valuePaintFont, valuePaint); } return bitmap; @@ -680,6 +681,6 @@ private static void DrawBlurredRoundRect(SKBitmap bitmap, SKRoundRect rect) paint.IsAntialias = true; paint.ImageFilter = _blurredFilter; - canvas.DrawBitmap(bitmap, 0, 0, paint); + canvas.DrawBitmap(bitmap, 0, 0, SKSamplingOptions.Default, paint); } } diff --git a/Controllers/UtilsImageController.cs b/Controllers/UtilsImageController.cs index 5818e22..a026dea 100644 --- a/Controllers/UtilsImageController.cs +++ b/Controllers/UtilsImageController.cs @@ -37,38 +37,39 @@ public async Task GenerateProgressBar(ProgressBar progressBar) using var barPaint = new SKPaintSafe(); barPaint.IsAntialias = true; barPaint.Shader = SKShader.CreateLinearGradient( - new SKPoint(0, 0), - new SKPoint(barWidth, 0), - [SKColor.Parse(progressBar.GradientColors[0]), SKColor.Parse(progressBar.GradientColors[1])], - [0, 1], - SKShaderTileMode.Repeat); + new SKPoint(0, 0), + new SKPoint(barWidth, 0), + [SKColor.Parse(progressBar.GradientColors[0]), SKColor.Parse(progressBar.GradientColors[1])], + [0, 1], + SKShaderTileMode.Repeat); canvas.DrawRoundRect(0, (bitmap.Height - 20) / 2f, barWidth, 20, 10, 10, barPaint); } var segoeFont = await assets.GetFont("Assets/Fonts/Segoe.ttf"); - var textBounds = new SKRect(); using var textPaint = new SKPaint(); textPaint.IsAntialias = true; textPaint.Color = SKColors.White; - textPaint.TextSize = 20; - textPaint.Typeface = segoeFont; + using var textPaintFont = new SKFont(); + textPaintFont.Size = 20; + textPaintFont.Typeface = segoeFont; - textPaint.MeasureText(progressBar.Text, ref textBounds); - canvas.DrawText(progressBar.Text, 500 + 5, bitmap.Height / 2f - textBounds.MidY, textPaint); + textPaintFont.MeasureText(progressBar.Text, out var textBounds, textPaint); + canvas.DrawText(progressBar.Text, 500 + 5, bitmap.Height / 2f - textBounds.MidY, SKTextAlign.Left, textPaintFont, textPaint); if (progressBar.BarText != null) { using var barTextPaint = new SKPaint(); barTextPaint.IsAntialias = true; barTextPaint.Color = SKColors.White; - barTextPaint.TextSize = 15; - barTextPaint.Typeface = segoeFont; + using var barTextPaintFont = new SKFont(); + barTextPaintFont.Size = 15; + barTextPaintFont.Typeface = segoeFont; - barTextPaint.MeasureText(progressBar.BarText, ref textBounds); + barTextPaintFont.MeasureText(progressBar.BarText, out textBounds, barTextPaint); canvas.DrawText(progressBar.BarText, (500 - textBounds.Width) / 2f, - bitmap.Height / 2f - textBounds.MidY, barTextPaint); + bitmap.Height / 2f - textBounds.MidY, SKTextAlign.Left, barTextPaintFont, barTextPaint); } var data = bitmap.Encode(SKEncodedImageFormat.Png, 100); @@ -99,7 +100,7 @@ await assets.GetBitmap( var mx = (drop.X + worldRadius) / (worldRadius * 2f) * bitmap.Width + xOffset; var my = (drop.Y + worldRadius) / (worldRadius * 2f) * bitmap.Height + yOffset; - canvas.DrawBitmap(markerBitmap, mx - markerBitmap!.Width / 2f, my - markerBitmap.Height); + canvas.DrawBitmap(markerBitmap, mx - markerBitmap!.Width / 2f, my - markerBitmap.Height, SKSamplingOptions.Default); var data = bitmap.Encode(SKEncodedImageFormat.Jpeg, 100); return File(data.AsStream(true), "image/jpeg"); diff --git a/EasyFortniteStats-ImageApi.csproj b/EasyFortniteStats-ImageApi.csproj index 5bca900..a9efbf2 100644 --- a/EasyFortniteStats-ImageApi.csproj +++ b/EasyFortniteStats-ImageApi.csproj @@ -11,8 +11,8 @@ - - + + diff --git a/ImageUtils.cs b/ImageUtils.cs index 04a8dff..aa0d964 100644 --- a/ImageUtils.cs +++ b/ImageUtils.cs @@ -24,11 +24,12 @@ public static async Task GenerateDiscordBox(SharedAssets assets, strin using var discordTagTextPaint = new SKPaint(); discordTagTextPaint.IsAntialias = true; discordTagTextPaint.Color = SKColors.White; - discordTagTextPaint.Typeface = segoeFont; - discordTagTextPaint.TextSize = 25 * resizeFactor; - var discordTagTextBounds = new SKRect(); - discordTagTextPaint.MeasureText(username, ref discordTagTextBounds); + using var discordTagTextPaintFont = new SKFont(); + discordTagTextPaintFont.Typeface = segoeFont; + discordTagTextPaintFont.Size = 25 * resizeFactor; + + discordTagTextPaintFont.MeasureText(username, out var discordTagTextBounds, discordTagTextPaint); var imageInfo = new SKImageInfo( (int)Math.Min(discordTagTextBounds.Width + (10 + 2 * 15 + 50) * resizeFactor, 459 * resizeFactor), @@ -51,17 +52,17 @@ public static async Task GenerateDiscordBox(SharedAssets assets, strin using var drawdiscordLogoPaint = new SKPaint(); drawdiscordLogoPaint.IsAntialias = true; - drawdiscordLogoPaint.FilterQuality = SKFilterQuality.High; - canvas.DrawBitmap(discordLogoBitmap, SKRect.Create(logoX, logoY, logoResizeWidth, logoResizeHeight), drawdiscordLogoPaint); + canvas.DrawBitmap(discordLogoBitmap, SKRect.Create(logoX, logoY, logoResizeWidth, logoResizeHeight), + new SKSamplingOptions(SKCubicResampler.Mitchell), drawdiscordLogoPaint); while (discordTagTextBounds.Width + (10 + 2 * 15 + 50) * resizeFactor > imageInfo.Width) { - discordTagTextPaint.TextSize--; - discordTagTextPaint.MeasureText(username, ref discordTagTextBounds); + discordTagTextPaintFont.Size--; + discordTagTextPaintFont.MeasureText(username, out discordTagTextBounds, discordTagTextPaint); } canvas.DrawText(username, (10 + 15) * resizeFactor + logoResizeWidth, - (float)imageInfo.Height / 2 - discordTagTextBounds.MidY, discordTagTextPaint); + (float)imageInfo.Height / 2 - discordTagTextBounds.MidY, SKTextAlign.Left, discordTagTextPaintFont, discordTagTextPaint); return bitmap; } @@ -81,7 +82,7 @@ private static SKBitmap RotateBitmap(SKBitmap bitmap, float angle) rotatedCanvas.Translate(rotatedWidth / 2f, rotatedHeight / 2f); rotatedCanvas.RotateDegrees(-angle); rotatedCanvas.Translate(-originalWidth / 2f, -originalHeight / 2f); - rotatedCanvas.DrawBitmap(bitmap, SKPoint.Empty); + rotatedCanvas.DrawBitmap(bitmap, SKPoint.Empty, SKSamplingOptions.Default); return rotatedBitmap; } @@ -97,12 +98,13 @@ public static SKBitmap GenerateRarityStripe(int width, SKColor rarityColor) paint.Color = rarityColor; paint.Style = SKPaintStyle.Fill; - using var path = new SKPath(); - path.MoveTo(0, imageInfo.Height - 5); - path.LineTo(imageInfo.Width, 0); - path.LineTo(imageInfo.Width, imageInfo.Height - 6); - path.LineTo(0, imageInfo.Height); - path.Close(); + using var pathBuilder = new SKPathBuilder(); + pathBuilder.MoveTo(0, imageInfo.Height - 5); + pathBuilder.LineTo(imageInfo.Width, 0); + pathBuilder.LineTo(imageInfo.Width, imageInfo.Height - 6); + pathBuilder.LineTo(0, imageInfo.Height); + pathBuilder.Close(); + using var path = pathBuilder.Detach(); canvas.DrawPath(path, paint); @@ -127,9 +129,10 @@ public static SKBitmap GenerateItemCardOverlay(int width, SKBitmap? icon = null) if (icon is not null) { using var rotatedVbucksBitmap = RotateBitmap(icon, -20); - using var resizedVBucksBitmap = rotatedVbucksBitmap.Resize(new SKImageInfo(47, 47), SKFilterQuality.Medium); + using var resizedVBucksBitmap = rotatedVbucksBitmap.Resize(new SKImageInfo(47, 47), + new SKSamplingOptions(SKFilterMode.Linear)); - canvas.DrawBitmap(resizedVBucksBitmap, new SKPoint(imageInfo.Width - 45, imageInfo.Height - 35)); + canvas.DrawBitmap(resizedVBucksBitmap, new SKPoint(imageInfo.Width - 45, imageInfo.Height - 35), SKSamplingOptions.Default); } using (var paint = new SKPaint()) @@ -138,12 +141,13 @@ public static SKBitmap GenerateItemCardOverlay(int width, SKBitmap? icon = null) paint.Color = new SKColor(30, 30, 30); paint.Style = SKPaintStyle.Fill; - using var path = new SKPath(); - path.MoveTo(0, imageInfo.Height - 29); - path.LineTo(imageInfo.Width, imageInfo.Height - 29); - path.LineTo(imageInfo.Width, imageInfo.Height - 25); - path.LineTo(0, imageInfo.Height - 24); - path.Close(); + using var pathBuilder = new SKPathBuilder(); + pathBuilder.MoveTo(0, imageInfo.Height - 29); + pathBuilder.LineTo(imageInfo.Width, imageInfo.Height - 29); + pathBuilder.LineTo(imageInfo.Width, imageInfo.Height - 25); + pathBuilder.LineTo(0, imageInfo.Height - 24); + pathBuilder.Close(); + using var path = pathBuilder.Detach(); canvas.DrawPath(path, paint); @@ -164,4 +168,4 @@ public static SKColor ParseColor(string hexString) } return SKColor.Parse(hexString); } -} \ No newline at end of file +} diff --git a/SKPaintSafe.cs b/SKPaintSafe.cs index c3efc14..118c9e7 100644 --- a/SKPaintSafe.cs +++ b/SKPaintSafe.cs @@ -30,4 +30,4 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); } -} \ No newline at end of file +} From a59e794e68cd6daa316fb4703ec8eacce825d35f Mon Sep 17 00:00:00 2001 From: Lucas Hardt Date: Mon, 10 Aug 2026 13:33:25 +0200 Subject: [PATCH 2/2] Inline out variable --- Controllers/LockerImageController.cs | 3 +-- Controllers/ShopImageController.cs | 9 +++------ ImageUtils.cs | 3 +-- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/Controllers/LockerImageController.cs b/Controllers/LockerImageController.cs index 57c3368..63d837b 100644 --- a/Controllers/LockerImageController.cs +++ b/Controllers/LockerImageController.cs @@ -345,8 +345,7 @@ private async Task GenerateFooter(float resizeFactor) //var text = "EasyFnStats.com".ToUpper(); const string text = "EASYFNSTATS.COM"; - SKRect textBounds; - textPaintFont.MeasureText(text, out textBounds, textPaint); + textPaintFont.MeasureText(text, out var textBounds, textPaint); var imageInfo = new SKImageInfo((int)((50 + 10 + 5 + 10) * resizeFactor + textBounds.Width), (int)(50 * resizeFactor)); diff --git a/Controllers/ShopImageController.cs b/Controllers/ShopImageController.cs index c1ef2ce..9c10c17 100644 --- a/Controllers/ShopImageController.cs +++ b/Controllers/ShopImageController.cs @@ -553,8 +553,7 @@ private async Task GenerateBanner(string text, IReadOnlyList c bannerPaintFont.Typeface = await assets.GetFont("Assets/Fonts/Fortnite-76BoldItalic.otf"); bannerPaint.Color = SKColor.Parse(colors[1]); - SKRect textBounds; - bannerPaintFont.MeasureText(text, out textBounds, bannerPaint); + bannerPaintFont.MeasureText(text, out var textBounds, bannerPaint); var maxTextWidth = maxWidth - 2 * 13; var imageInfo = new SKImageInfo(Math.Min(2 * 13 + (int)textBounds.Width, maxWidth), 34); @@ -781,8 +780,7 @@ private static string[] SplitNameText(string text, int maxWidth, SKFont paintFon foreach (Match match in matches) { var line = lines[currentLine]; - SKRect bounds; - paintFont.MeasureText(line + match.Value, out bounds, paint); + paintFont.MeasureText(line + match.Value, out var bounds, paint); if (bounds.Width > maxWidth) currentLine++; if (currentLine >= 2) { @@ -796,8 +794,7 @@ private static string[] SplitNameText(string text, int maxWidth, SKFont paintFon // Adjust lines that are too long and add ellipsis foreach (var line in lines) { - SKRect textBounds; - paintFont.MeasureText(line.ToString(), out textBounds, paint); + paintFont.MeasureText(line.ToString(), out var textBounds, paint); if (textBounds.Width <= maxWidth) continue; while (textBounds.Width > maxWidth) diff --git a/ImageUtils.cs b/ImageUtils.cs index 9604d62..7269b8e 100644 --- a/ImageUtils.cs +++ b/ImageUtils.cs @@ -28,8 +28,7 @@ public static async Task GenerateDiscordBox(SharedAssets assets, strin discordTagTextPaintFont.Typeface = segoeFont; discordTagTextPaintFont.Size = 25 * resizeFactor; - SKRect discordTagTextBounds; - discordTagTextPaintFont.MeasureText(username, out discordTagTextBounds, discordTagTextPaint); + discordTagTextPaintFont.MeasureText(username, out var discordTagTextBounds, discordTagTextPaint); var imageInfo = new SKImageInfo( (int)Math.Min(discordTagTextBounds.Width + (10 + 2 * 15 + 50) * resizeFactor, 459 * resizeFactor),