A game framework written with osu! in mind.
at master 137 lines 4.3 kB view raw
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. 2// See the LICENCE file in the repository root for full licence text. 3 4using System; 5using System.IO; 6using System.Runtime.InteropServices; 7using osu.Framework.Extensions.ImageExtensions; 8using osu.Framework.Graphics.OpenGL; 9using osu.Framework.Graphics.OpenGL.Buffers; 10using osu.Framework.Graphics.Primitives; 11using osu.Framework.Logging; 12using osuTK.Graphics.ES30; 13using SixLabors.ImageSharp; 14using SixLabors.ImageSharp.PixelFormats; 15using StbiSharp; 16 17namespace osu.Framework.Graphics.Textures 18{ 19 /// <summary> 20 /// Low level class for queueing texture uploads to the GPU. 21 /// Should be manually disposed if not queued for upload via <see cref="Texture.SetData"/>. 22 /// </summary> 23 public class TextureUpload : ITextureUpload 24 { 25 /// <summary> 26 /// The target mipmap level to upload into. 27 /// </summary> 28 public int Level { get; set; } 29 30 /// <summary> 31 /// The texture format for this upload. 32 /// </summary> 33 public PixelFormat Format => PixelFormat.Rgba; 34 35 /// <summary> 36 /// The target bounds for this upload. If not specified, will assume to be (0, 0, width, height). 37 /// </summary> 38 public RectangleI Bounds { get; set; } 39 40 public ReadOnlySpan<Rgba32> Data => pixelMemory.Span; 41 42 public int Width => image?.Width ?? 0; 43 44 public int Height => image?.Height ?? 0; 45 46 /// <summary> 47 /// The backing texture. A handle is kept to avoid early GC. 48 /// </summary> 49 private readonly Image<Rgba32> image; 50 51 private ReadOnlyPixelMemory<Rgba32> pixelMemory; 52 53 /// <summary> 54 /// Create an upload from a <see cref="TextureUpload"/>. This is the preferred method. 55 /// </summary> 56 /// <param name="image">The texture to upload.</param> 57 public TextureUpload(Image<Rgba32> image) 58 { 59 this.image = image; 60 61 if (image.Width > GLWrapper.MaxTextureSize || image.Height > GLWrapper.MaxTextureSize) 62 throw new TextureTooLargeForGLException(); 63 64 pixelMemory = image.CreateReadOnlyPixelMemory(); 65 } 66 67 /// <summary> 68 /// Create an upload from an arbitrary image stream. 69 /// Note that this bypasses per-platform image loading optimisations. 70 /// Use <see cref="TextureLoaderStore"/> as provided from GameHost where possible. 71 /// </summary> 72 /// <param name="stream">The image content.</param> 73 public TextureUpload(Stream stream) 74 : this(LoadFromStream<Rgba32>(stream)) 75 { 76 } 77 78 private static bool stbiNotFound; 79 80 internal static Image<TPixel> LoadFromStream<TPixel>(Stream stream) where TPixel : unmanaged, IPixel<TPixel> 81 { 82 if (stbiNotFound) 83 return Image.Load<TPixel>(stream); 84 85 long initialPos = stream.Position; 86 87 try 88 { 89 using (var m = new MemoryStream()) 90 { 91 stream.CopyTo(m); 92 using (var stbiImage = Stbi.LoadFromMemory(m, 4)) 93 return Image.LoadPixelData(MemoryMarshal.Cast<byte, TPixel>(stbiImage.Data), stbiImage.Width, stbiImage.Height); 94 } 95 } 96 catch (Exception e) 97 { 98 if (e is DllNotFoundException) 99 stbiNotFound = true; 100 101 Logger.Log($"Texture could not be loaded via STB; falling back to ImageSharp: {e.Message}"); 102 stream.Position = initialPos; 103 return Image.Load<TPixel>(stream); 104 } 105 } 106 107 /// <summary> 108 /// Create an empty upload. Used by <see cref="FrameBuffer"/> for initialisation. 109 /// </summary> 110 internal TextureUpload() 111 { 112 } 113 114 #region IDisposable Support 115 116 private bool disposed; 117 118 public void Dispose() 119 { 120 Dispose(true); 121 GC.SuppressFinalize(this); 122 } 123 124 protected virtual void Dispose(bool isDisposing) 125 { 126 if (disposed) 127 return; 128 129 image?.Dispose(); 130 pixelMemory.Dispose(); 131 132 disposed = true; 133 } 134 135 #endregion 136 } 137}