using System; using System.Collections.Generic; using System.IO; using Content.Shared.Decals; using Robust.Client.ResourceManagement; using Robust.Client.Utility; using Robust.Shared.Prototypes; using Robust.Shared.Timing; using Robust.Shared.Utility; using SixLabors.ImageSharp; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using static Robust.UnitTesting.RobustIntegrationTest; namespace Content.MapRenderer.Painters; public sealed class DecalPainter { private readonly IResourceCache _cResourceCache; private readonly IPrototypeManager _sPrototypeManager; private readonly Dictionary _decalTextures = new(); public DecalPainter(ClientIntegrationInstance client, ServerIntegrationInstance server) { _cResourceCache = client.ResolveDependency(); _sPrototypeManager = server.ResolveDependency(); } public void Run(Image canvas, Span decals) { var stopwatch = new Stopwatch(); stopwatch.Start(); decals.Sort(Comparer.Create((x, y) => x.Decal.ZIndex.CompareTo(y.Decal.ZIndex))); if (_decalTextures.Count == 0) { foreach (var proto in _sPrototypeManager.EnumeratePrototypes()) { _decalTextures.Add(proto.ID, proto.Sprite); } } foreach (var decal in decals) { Run(canvas, decal); } Console.WriteLine($"{nameof(DecalPainter)} painted {decals.Length} decals in {(int) stopwatch.Elapsed.TotalMilliseconds} ms"); } private void Run(Image canvas, DecalData data) { var decal = data.Decal; if (!_decalTextures.TryGetValue(decal.Id, out var sprite)) { Console.WriteLine($"Decal {decal.Id} did not have an associated prototype."); return; } Stream stream; if (sprite is SpriteSpecifier.Texture texture) { stream = _cResourceCache.ContentFileRead(texture.TexturePath); } else if (sprite is SpriteSpecifier.Rsi rsi) { var path = $"{rsi.RsiPath}/{rsi.RsiState}.png"; if (!path.StartsWith("/Textures")) { path = $"/Textures/{path}"; } stream = _cResourceCache.ContentFileRead(path); } else { // Don't support return; } var image = Image.Load(stream); image.Mutate(o => o.Rotate((float) -decal.Angle.Degrees)); var coloredImage = new Image(image.Width, image.Height); Color color = decal.Color?.ConvertImgSharp() ?? Color.White; coloredImage.Mutate(o => o.BackgroundColor(color)); image.Mutate(o => o .DrawImage(coloredImage, PixelColorBlendingMode.Multiply, PixelAlphaCompositionMode.SrcAtop, 1.0f) .Flip(FlipMode.Vertical)); // Very unsure why the - 1 is needed in the first place but all decals are off by exactly one pixel otherwise // Woohoo! canvas.Mutate(o => o.DrawImage(image, new Point((int) data.X, (int) data.Y - 1), 1.0f)); } }