BitmapFontProcessor.cs 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using Microsoft.Xna.Framework.Content.Pipeline;
  5. using MonoGame.Extended.Content.BitmapFonts;
  6. namespace MonoGame.Extended.Content.Pipeline.BitmapFonts
  7. {
  8. [ContentProcessor(DisplayName = "BMFont Processor - MonoGame.Extended")]
  9. public class BitmapFontProcessor : ContentProcessor<ContentImporterResult<BitmapFontFileContent>, BitmapFontProcessorResult>
  10. {
  11. public override BitmapFontProcessorResult Process(ContentImporterResult<BitmapFontFileContent> importerResult, ContentProcessorContext context)
  12. {
  13. try
  14. {
  15. BitmapFontFileContent bmfFile = importerResult.Data;
  16. ValidateKernings(bmfFile, context);
  17. var result = new BitmapFontProcessorResult(bmfFile);
  18. foreach (var page in bmfFile.Pages)
  19. {
  20. context.AddDependency(Path.GetFileName(page));
  21. result.TextureAssets.Add(Path.GetFileNameWithoutExtension(page));
  22. }
  23. return result;
  24. }
  25. catch (Exception ex)
  26. {
  27. context.Logger.LogMessage("Error {0}", ex);
  28. throw;
  29. }
  30. }
  31. private readonly record struct KerningPair(uint First, uint Second);
  32. private readonly record struct KerningEntry(short Amount, int Index);
  33. private readonly record struct DuplicateKerning(uint First, uint Second, short FirstAmount, short DuplicateAmount, int FirstIndex, int DuplicateIndex);
  34. private void ValidateKernings(BitmapFontFileContent bmfFile, ContentProcessorContext context)
  35. {
  36. if(bmfFile.Kernings.Count == 0)
  37. {
  38. return;
  39. }
  40. Dictionary<KerningPair, KerningEntry> seenPairs = [];
  41. List<DuplicateKerning> duplicates = [];
  42. for(int i = 0; i < bmfFile.Kernings.Count; i++)
  43. {
  44. BitmapFontFileContent.KerningPairsBlock kerning = bmfFile.Kernings[i];
  45. KerningPair pair = new KerningPair(kerning.First, kerning.Second);
  46. if(seenPairs.TryGetValue(pair, out KerningEntry existing))
  47. {
  48. duplicates.Add(new DuplicateKerning(pair.First, pair.Second, existing.Amount, kerning.Amount, existing.Index, i));
  49. }
  50. else
  51. {
  52. seenPairs[pair] = new KerningEntry(kerning.Amount, i);
  53. }
  54. }
  55. if(duplicates.Count > 0)
  56. {
  57. context.Logger.LogWarning(
  58. string.Empty,
  59. new ContentIdentity(bmfFile.Path),
  60. $"""
  61. BMFont file contains {duplicates.Count} duplicate kerning pair(s).
  62. This may cause runtime errors. Each character pair should only have one kerning entry.
  63. Please regenerate or fix the font file
  64. """
  65. );
  66. foreach(var duplicate in duplicates)
  67. {
  68. char firstChar = duplicate.First >= 32 && duplicate.First < 127
  69. ? (char)duplicate.First
  70. : '?';
  71. char secondChar = duplicate.Second >= 32 && duplicate.Second < 127
  72. ? (char)duplicate.Second
  73. : '?';
  74. context.Logger.LogWarning(
  75. string.Empty,
  76. new ContentIdentity(bmfFile.Path),
  77. $"""
  78. Duplicate kerning: Character {duplicate.First} ('{firstChar}') -> {duplicate.Second} ('{secondChar}')
  79. First entry (index {duplicate.FirstIndex}): amount={duplicate.FirstAmount}
  80. Duplicate etnry (index {duplicate.DuplicateIndex}): amount={duplicate.DuplicateIndex}")
  81. """
  82. );
  83. }
  84. }
  85. }
  86. }
  87. }