TextureFactory.cs 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. using Microsoft.Xna.Framework.Graphics;
  7. namespace SharpGLTF.Runtime.Pipeline
  8. {
  9. /// <summary>
  10. /// Factory used to convert images into <see cref="Texture2D"/> objects.
  11. /// </summary>
  12. /// <remarks>
  13. /// By default, this factory is only able to read images using <see cref="Texture2D.FromStream(GraphicsDevice, System.IO.Stream)"/>;<br/>
  14. /// In order to add support for more formats, create a derived class and register it at <see cref="InstanceBuilder"/>
  15. /// </remarks>
  16. public class TextureFactory
  17. {
  18. #region lifecycle
  19. /// <summary>
  20. /// Register here your own <see cref="TextureFactory"/> derived class to override texture creation
  21. /// </summary>
  22. public static Func<GraphicsDevice, GraphicsResourceTracker, TextureFactory> InstanceBuilder { get; set; }
  23. public static TextureFactory Create(GraphicsDevice device, GraphicsResourceTracker disposables)
  24. {
  25. ArgumentNullException.ThrowIfNull(device);
  26. ArgumentNullException.ThrowIfNull(disposables);
  27. var tf = InstanceBuilder?.Invoke(device, disposables);
  28. tf ??= new TextureFactory(device, disposables);
  29. return tf;
  30. }
  31. protected TextureFactory(GraphicsDevice device, GraphicsResourceTracker disposables)
  32. {
  33. _Device = device;
  34. _Disposables = disposables;
  35. }
  36. #endregion
  37. #region data
  38. private readonly GraphicsDevice _Device;
  39. private readonly GraphicsResourceTracker _Disposables;
  40. // temporary cache to prevent creating the same texture multiple times
  41. private readonly Dictionary<Memory.MemoryImage, Texture2D> _Textures = new Dictionary<Memory.MemoryImage, Texture2D>();
  42. #endregion
  43. #region API
  44. /// <summary>
  45. /// Some effects require a white texture
  46. /// </summary>
  47. /// <returns></returns>
  48. public Texture2D UseWhiteImage()
  49. {
  50. var tex = _Disposables
  51. .Disposables
  52. .OfType<Texture2D>()
  53. .FirstOrDefault(item => item.Name == "_InternalSolidWhite");
  54. if (tex != null) return tex;
  55. const string solidWhitePNg = "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAFHpUWHRUaXRsZQAACJkrz8gsSQUABoACIippo0oAAAAoelRYdEF1dGhvcgAACJkLy0xOzStJVQhIzUtMSS1WcCzKTc1Lzy8BAG89CQyAoFAQAAAAGklEQVQoz2P8//8/AymAiYFEMKphVMPQ0QAAVW0DHZ8uFaIAAAAASUVORK5CYII=";
  56. var toBytes = Convert.FromBase64String(solidWhitePNg);
  57. return UseTexture(new ArraySegment<byte>(toBytes), "_InternalSolidWhite");
  58. }
  59. public Texture2D UseTexture(Memory.MemoryImage image, string name = null)
  60. {
  61. if (_Device == null) throw new InvalidOperationException();
  62. if (!image.IsValid) return null;
  63. if (image.MimeType == "image/webp") return UseWhiteImage();
  64. if (image.MimeType == "image/ktx2") return UseWhiteImage();
  65. if (_Textures.TryGetValue(image, out Texture2D tex)) return tex;
  66. using (var m = image.Open())
  67. {
  68. tex = LoadTexture(_Device, m, image.MimeType);
  69. _Disposables.AddDisposable(tex);
  70. tex.Name = name;
  71. _Textures[image] = tex;
  72. return tex;
  73. }
  74. }
  75. #endregion
  76. #region Overridable API
  77. /// <summary>
  78. /// Override this method to support loading images not supported by MonoGame (KTX2, WebP)
  79. /// </summary>
  80. protected virtual Texture2D LoadTexture(GraphicsDevice device, System.IO.Stream image, string imageMimeType)
  81. {
  82. if (imageMimeType == "image/webp") throw new NotSupportedException("webp images not supported");
  83. if (imageMimeType == "image/ktx2") throw new NotSupportedException("ktx2 images not supported");
  84. return Texture2D.FromStream(device, image);
  85. }
  86. #endregion
  87. }
  88. }