Skip to content
Merged

Beta #114

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 0 additions & 11 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,6 @@ dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case

dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_style_operator_placement_when_wrapping = beginning_of_line
tab_width = 4
indent_size = 4
Expand All @@ -64,7 +60,6 @@ dotnet_style_prefer_inferred_tuple_names = true:suggestion
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
dotnet_style_prefer_compound_assignment = true:suggestion
dotnet_style_prefer_simplified_interpolation = true:suggestion
dotnet_style_namespace_match_folder = true:suggestion
indent_style = space

[*.cs]
Expand All @@ -74,8 +69,6 @@ csharp_using_directive_placement = outside_namespace:silent
csharp_prefer_simple_using_statement = true:suggestion
csharp_prefer_braces = true:silent
csharp_style_namespace_declarations = file_scoped:suggestion
csharp_style_prefer_method_group_conversion = true:silent
csharp_style_prefer_top_level_statements = true:silent
csharp_style_expression_bodied_methods = false:silent
csharp_style_expression_bodied_constructors = false:silent
csharp_style_expression_bodied_operators = false:silent
Expand All @@ -85,13 +78,9 @@ csharp_style_expression_bodied_accessors = true:silent
csharp_style_expression_bodied_lambdas = true:silent
csharp_style_expression_bodied_local_functions = false:silent
csharp_style_throw_expression = true:suggestion
csharp_style_prefer_null_check_over_type_check = true:suggestion
csharp_prefer_simple_default_expression = true:suggestion
csharp_style_prefer_local_over_anonymous_function = true:suggestion
csharp_style_prefer_index_operator = true:suggestion
csharp_style_prefer_range_operator = true:suggestion
csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion
csharp_style_prefer_tuple_swap = true:suggestion
csharp_style_prefer_utf8_string_literals = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
csharp_style_deconstructed_variable_declaration = true:suggestion
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -402,3 +402,6 @@ data/

# Fonts, as we don't have license to distribute them
Assets/Fonts/

# Mac DS_Store files
.DS_Store
82 changes: 52 additions & 30 deletions Controllers/LockerImageController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ namespace EasyFortniteStats_ImageApi.Controllers;
public class AccountImageController(
IHttpClientFactory clientFactory,
AsyncKeyedLocker<string> namedLock,
SharedAssets assets)
SharedAssets assets,
ILogger<AccountImageController> logger)
: ControllerBase
{
private const string BASE_ITEM_IMAGE_PATH = "data/images/locker/items";
Expand All @@ -36,8 +37,9 @@ public class AccountImageController(
[HttpPost]
public async Task<IActionResult> Post(Locker locker, [FromQuery] bool? lossless)
{
Console.WriteLine(
$"Locker image request | Name = {locker.PlayerName} | Locale = {locker.Locale} | Items = {locker.Items.Length}");
logger.LogInformation(
"Locker image request received | Name = {PlayerName} | Locale = {Locale} | Items = {Items}",
locker.PlayerName, locker.Locale, locker.Items.Length);
var lockKey = $"locker_{locker.RequestId}";
using (await namedLock.LockAsync(lockKey).ConfigureAwait(false))
{
Expand Down Expand Up @@ -79,11 +81,12 @@ private async Task<SKBitmap> GenerateImage(Locker locker)

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 icon = await assets.GetBitmap("Assets/Images/Locker/Icon.png"); // don't dispose
var resize = (int)(50 * uiResizingFactor);
using var resizeIcon = icon!.Resize(new SKImageInfo(resize, resize), SKSamplingOptions.Default);
using var resizeIcon = icon!.Resize(new SKImageInfo(resize, resize), SKFilterQuality.High);
canvas.DrawBitmap(resizeIcon, 50, 50);

using var splitPaint = new SKPaint();
Expand All @@ -96,12 +99,14 @@ private async Task<SKBitmap> GenerateImage(Locker locker)
splitPaint);

using var namePaint = new SKPaint();
using var nameFont = new SKFont(segoeFont, nameFontSize);
namePaint.IsAntialias = true;
namePaint.Color = SKColors.White;
namePaint.Typeface = segoeFont;
namePaint.TextSize = nameFontSize;
namePaint.FilterQuality = SKFilterQuality.Medium;

nameFont.MeasureText(locker.PlayerName, out var textBounds);
canvas.DrawText(locker.PlayerName, 50 + resizeIcon.Width + splitWidth * 3, 58 - textBounds.Top, nameFont, namePaint);
namePaint.MeasureText(locker.PlayerName, ref textBounds);
canvas.DrawText(locker.PlayerName, 50 + resizeIcon.Width + splitWidth * 3, 58 - textBounds.Top, namePaint);

using var discordBoxBitmap = await ImageUtils.GenerateDiscordBox(assets, locker.UserName, uiResizingFactor);
canvas.DrawBitmap(discordBoxBitmap, imageInfo.Width - 50 - discordBoxBitmap.Width, 39);
Expand Down Expand Up @@ -156,15 +161,17 @@ await Parallel.ForEachAsync(locker.Items, options, async (item, token) =>
}
catch (HttpRequestException e2)
{
Console.WriteLine(
$"Failed to download image with status {e2.StatusCode} for {item.Name} ({item.ImageUrl}) ");
logger.LogWarning(
"Failed to download image with status {StatusCode} for {Name} ({ImageUrl}) ",
e2.StatusCode, item.Name, item.ImageUrl);
itemImageBytes = null;
}
}
catch (HttpRequestException e)
{
Console.WriteLine(
$"Failed to download image with status {e.StatusCode} for {item.Name} ({item.ImageUrl}) ");
logger.LogWarning(
"Failed to download image with status {StatusCode} for {Name} ({ImageUrl}) ",
e.StatusCode, item.Name, item.ImageUrl);
itemImageBytes = null;
}

Expand All @@ -173,7 +180,7 @@ await Parallel.ForEachAsync(locker.Items, options, async (item, token) =>
var itemImageRaw = SKBitmap.Decode(itemImageBytes);
if (itemImageRaw.Width != 256 || itemImageRaw.Height != 256)
{
itemImage = itemImageRaw.Resize(new SKImageInfo(256, 256), SKSamplingOptions.Default);
itemImage = itemImageRaw.Resize(new SKImageInfo(256, 256), SKFilterQuality.Medium);
}
else
{
Expand Down Expand Up @@ -214,13 +221,17 @@ private async Task<SKBitmap> GenerateItemCard(LockerItem lockerItem, SKBitmap? i
else
{
using var questionmarkPaint = new SKPaint();
using var questionmarkFont = new SKFont(await assets.GetFont("Assets/Fonts/Fortnite-86Bold.otf"), 256.0f);
questionmarkPaint.IsAntialias = true;
questionmarkPaint.Color = SKColors.White;

questionmarkFont.MeasureText("?", out var questionmarkTextBounds);

canvas.DrawText("?", (float)bitmap.Width / 2, (float)bitmap.Height / 2 + questionmarkTextBounds.Height / 2, SKTextAlign.Center, questionmarkFont, questionmarkPaint);
questionmarkPaint.Typeface = await assets.GetFont("Assets/Fonts/Fortnite-86Bold.otf");
questionmarkPaint.TextSize = 256.0f;
questionmarkPaint.TextAlign = SKTextAlign.Center;

var questionmarkTextBounds = new SKRect();
questionmarkPaint.MeasureText("?", ref questionmarkTextBounds);

canvas.DrawText("?", (float)bitmap.Width / 2, (float)bitmap.Height / 2 + questionmarkTextBounds.Height / 2,
questionmarkPaint);
}

var typeIcon = lockerItem.SourceType != SourceType.Other
Expand All @@ -238,31 +249,40 @@ private async Task<SKBitmap> GenerateItemCard(LockerItem lockerItem, SKBitmap? i
var fortniteFont = await assets.GetFont("Assets/Fonts/Fortnite.ttf"); // don't dispose

using var namePaint = new SKPaint();
using var nameFont = new SKFont(fortniteFont, 18.0f);
namePaint.IsAntialias = true;
namePaint.TextSize = 18.0f;
namePaint.Color = SKColors.White;
namePaint.Typeface = fortniteFont;
namePaint.TextAlign = SKTextAlign.Center;

nameFont.MeasureText(lockerItem.Name, out var entryNameTextBounds);
canvas.DrawText(lockerItem.Name, (float)bitmap.Width / 2, bitmap.Height - 59 + entryNameTextBounds.Height, SKTextAlign.Center, nameFont, namePaint);
var entryNameTextBounds = new SKRect();
namePaint.MeasureText(lockerItem.Name, ref entryNameTextBounds);
canvas.DrawText(lockerItem.Name, (float)bitmap.Width / 2, bitmap.Height - 59 + entryNameTextBounds.Height,
namePaint);

using var descriptionPaint = new SKPaint();
using var descriptionFont = new SKFont(fortniteFont, 15.0f);
descriptionPaint.IsAntialias = true;
descriptionPaint.TextSize = 15.0f;
descriptionPaint.Color = SKColor.Parse(lockerItem.RarityColor);
descriptionPaint.Typeface = fortniteFont;
descriptionPaint.TextAlign = SKTextAlign.Center;

descriptionFont.MeasureText(lockerItem.Description, out entryNameTextBounds);
descriptionPaint.MeasureText(lockerItem.Description, ref entryNameTextBounds);
canvas.DrawText(lockerItem.Description, (float)bitmap.Width / 2,
bitmap.Height - 42 + entryNameTextBounds.Height, SKTextAlign.Center, descriptionFont, descriptionPaint);
bitmap.Height - 42 + entryNameTextBounds.Height, descriptionPaint);

using var sourcePaint = new SKPaint();
using var sourceFont = new SKFont(fortniteFont, 15.0f);
sourcePaint.IsAntialias = true;
sourcePaint.TextSize = 15.0f;
sourcePaint.Color = SKColors.White;
sourcePaint.Typeface = fortniteFont;
sourcePaint.TextAlign = SKTextAlign.Right;

var fontOffset = lockerItem.SourceType == SourceType.Other ? 10 : 42;

sourceFont.MeasureText(lockerItem.Source, out entryNameTextBounds);
canvas.DrawText(lockerItem.Source, bitmap.Width - fontOffset, bitmap.Height - entryNameTextBounds.Height + 8, SKTextAlign.Right, sourceFont, sourcePaint);
sourcePaint.MeasureText(lockerItem.Source, ref entryNameTextBounds);
canvas.DrawText(lockerItem.Source, bitmap.Width - fontOffset, bitmap.Height - entryNameTextBounds.Height + 8,
sourcePaint);

return bitmap;
}
Expand All @@ -272,14 +292,16 @@ private async Task<SKBitmap> GenerateFooter(float resizeFactor)
var poppinsFont = await assets.GetFont("Assets/Fonts/Poppins.ttf"); // don't dispose

using var textPaint = new SKPaint();
using var textFont = new SKFont(poppinsFont, 40.0f * resizeFactor);
textPaint.IsAntialias = true;
textPaint.TextSize = 40.0f * resizeFactor;
textPaint.Color = SKColors.White;
textPaint.Typeface = poppinsFont;

//var text = "EasyFnStats.com".ToUpper();
const string text = "EASYFNSTATS.COM";

textFont.MeasureText(text, out var textBounds);
var textBounds = new SKRect();
textPaint.MeasureText(text, ref textBounds);

var imageInfo = new SKImageInfo((int)((50 + 10 + 5 + 10) * resizeFactor + textBounds.Width),
(int)(50 * resizeFactor));
Expand All @@ -288,7 +310,7 @@ private async Task<SKBitmap> GenerateFooter(float resizeFactor)

var logoBitmap = await assets.GetBitmap("Assets/Images/Logo.png"); // don't dispose
var logoBitmapResize =
logoBitmap!.Resize(new SKImageInfo(imageInfo.Height, imageInfo.Height), SKSamplingOptions.Default);
logoBitmap!.Resize(new SKImageInfo(imageInfo.Height, imageInfo.Height), SKFilterQuality.High);
canvas.DrawBitmap(logoBitmapResize, new SKPoint(0, 0));

var splitR = 3 * resizeFactor;
Expand All @@ -299,7 +321,7 @@ private async Task<SKBitmap> 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, textFont, textPaint);
canvas.DrawText(text, (50 + 10 + 5 + 10) * resizeFactor, (imageInfo.Height + textBounds.Height) / 2, textPaint);

return bitmap;
}
Expand Down
Loading