1
0

DecalPainter.cs 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using Content.Shared.Decals;
  5. using Robust.Client.ResourceManagement;
  6. using Robust.Client.Utility;
  7. using Robust.Shared.ContentPack;
  8. using Robust.Shared.Prototypes;
  9. using Robust.Shared.Timing;
  10. using Robust.Shared.Utility;
  11. using SixLabors.ImageSharp;
  12. using SixLabors.ImageSharp.PixelFormats;
  13. using SixLabors.ImageSharp.Processing;
  14. using static Robust.UnitTesting.RobustIntegrationTest;
  15. namespace Content.MapRenderer.Painters;
  16. public sealed class DecalPainter
  17. {
  18. private readonly IResourceManager _resManager;
  19. private readonly IPrototypeManager _sPrototypeManager;
  20. private readonly Dictionary<string, SpriteSpecifier> _decalTextures = new();
  21. public DecalPainter(ClientIntegrationInstance client, ServerIntegrationInstance server)
  22. {
  23. _resManager = client.ResolveDependency<IResourceManager>();
  24. _sPrototypeManager = server.ResolveDependency<IPrototypeManager>();
  25. }
  26. public void Run(Image canvas, Span<DecalData> decals)
  27. {
  28. var stopwatch = new Stopwatch();
  29. stopwatch.Start();
  30. decals.Sort(Comparer<DecalData>.Create((x, y) => x.Decal.ZIndex.CompareTo(y.Decal.ZIndex)));
  31. if (_decalTextures.Count == 0)
  32. {
  33. foreach (var proto in _sPrototypeManager.EnumeratePrototypes<DecalPrototype>())
  34. {
  35. _decalTextures.Add(proto.ID, proto.Sprite);
  36. }
  37. }
  38. foreach (var decal in decals)
  39. {
  40. Run(canvas, decal);
  41. }
  42. Console.WriteLine($"{nameof(DecalPainter)} painted {decals.Length} decals in {(int) stopwatch.Elapsed.TotalMilliseconds} ms");
  43. }
  44. private void Run(Image canvas, DecalData data)
  45. {
  46. var decal = data.Decal;
  47. if (!_decalTextures.TryGetValue(decal.Id, out var sprite))
  48. {
  49. Console.WriteLine($"Decal {decal.Id} did not have an associated prototype.");
  50. return;
  51. }
  52. Stream stream;
  53. if (sprite is SpriteSpecifier.Texture texture)
  54. {
  55. stream = _resManager.ContentFileRead(texture.TexturePath);
  56. }
  57. else if (sprite is SpriteSpecifier.Rsi rsi)
  58. {
  59. var path = $"{rsi.RsiPath}/{rsi.RsiState}.png";
  60. if (!path.StartsWith("/Textures"))
  61. {
  62. path = $"/Textures/{path}";
  63. }
  64. stream = _resManager.ContentFileRead(path);
  65. }
  66. else
  67. {
  68. // Don't support
  69. return;
  70. }
  71. var image = Image.Load<Rgba32>(stream);
  72. image.Mutate(o => o.Rotate((float) -decal.Angle.Degrees));
  73. var coloredImage = new Image<Rgba32>(image.Width, image.Height);
  74. Color color = decal.Color?.WithAlpha(byte.MaxValue).ConvertImgSharp() ?? Color.White; // remove the encoded color alpha here
  75. var alpha = decal.Color?.A ?? 1; // get the alpha separately so we can use it in DrawImage
  76. coloredImage.Mutate(o => o.BackgroundColor(color));
  77. image.Mutate(o => o
  78. .DrawImage(coloredImage, PixelColorBlendingMode.Multiply, PixelAlphaCompositionMode.SrcAtop, 1.0f)
  79. .Flip(FlipMode.Vertical));
  80. // Very unsure why the - 1 is needed in the first place but all decals are off by exactly one pixel otherwise
  81. // Woohoo!
  82. canvas.Mutate(o => o.DrawImage(image, new Point((int) data.X, (int) data.Y - 1), alpha));
  83. }
  84. }