#region License /* FNA - XNA4 Reimplementation for Desktop Platforms * Copyright 2009-2022 Ethan Lee and the MonoGame Team * * Released under the Microsoft Public License. * See LICENSE for details. */ #endregion #region Using Statements using System; using System.IO; #endregion namespace Microsoft.Xna.Framework.Graphics { public abstract class Texture : GraphicsResource { #region Public Properties public SurfaceFormat Format { get; protected set; } public int LevelCount { get; protected set; } #endregion #region Internal FNA3D Variables internal IntPtr texture; #endregion #region Protected Dispose Method protected override void Dispose(bool disposing) { if (!IsDisposed) { GraphicsDevice.Textures.RemoveDisposedTexture(this); GraphicsDevice.VertexTextures.RemoveDisposedTexture(this); FNA3D.FNA3D_AddDisposeTexture( GraphicsDevice.GLDevice, texture ); } base.Dispose(disposing); } #endregion #region Internal Context Reset Method internal protected override void GraphicsDeviceResetting() { // FIXME: Do we even want to bother with DeviceResetting for GL? -flibit } #endregion #region Static SurfaceFormat Size Methods protected static int GetBlockSizeSquared(SurfaceFormat format) { switch (format) { case SurfaceFormat.Dxt1: case SurfaceFormat.Dxt3: case SurfaceFormat.Dxt5: case SurfaceFormat.Dxt5SrgbEXT: case SurfaceFormat.Bc7EXT: case SurfaceFormat.Bc7SrgbEXT: return 16; case SurfaceFormat.Alpha8: case SurfaceFormat.Bgr565: case SurfaceFormat.Bgra4444: case SurfaceFormat.Bgra5551: case SurfaceFormat.HalfSingle: case SurfaceFormat.NormalizedByte2: case SurfaceFormat.Color: case SurfaceFormat.Single: case SurfaceFormat.Rg32: case SurfaceFormat.HalfVector2: case SurfaceFormat.NormalizedByte4: case SurfaceFormat.Rgba1010102: case SurfaceFormat.ColorBgraEXT: case SurfaceFormat.ColorSrgbEXT: case SurfaceFormat.HalfVector4: case SurfaceFormat.Rgba64: case SurfaceFormat.Vector2: case SurfaceFormat.HdrBlendable: case SurfaceFormat.Vector4: return 1; default: throw new ArgumentException("Should be a value defined in SurfaceFormat", "Format"); } } internal static int GetFormatSize(SurfaceFormat format) { switch (format) { case SurfaceFormat.Dxt1: return 8; case SurfaceFormat.Dxt3: case SurfaceFormat.Dxt5: case SurfaceFormat.Dxt5SrgbEXT: case SurfaceFormat.Bc7EXT: case SurfaceFormat.Bc7SrgbEXT: return 16; case SurfaceFormat.Alpha8: return 1; case SurfaceFormat.Bgr565: case SurfaceFormat.Bgra4444: case SurfaceFormat.Bgra5551: case SurfaceFormat.HalfSingle: case SurfaceFormat.NormalizedByte2: return 2; case SurfaceFormat.Color: case SurfaceFormat.Single: case SurfaceFormat.Rg32: case SurfaceFormat.HalfVector2: case SurfaceFormat.NormalizedByte4: case SurfaceFormat.Rgba1010102: case SurfaceFormat.ColorBgraEXT: case SurfaceFormat.ColorSrgbEXT: return 4; case SurfaceFormat.HalfVector4: case SurfaceFormat.Rgba64: case SurfaceFormat.Vector2: case SurfaceFormat.HdrBlendable: return 8; case SurfaceFormat.Vector4: return 16; default: throw new ArgumentException("Should be a value defined in SurfaceFormat", "Format"); } } internal static int GetPixelStoreAlignment(SurfaceFormat format) { /* * https://github.com/FNA-XNA/FNA/pull/238 * https://www.khronos.org/registry/OpenGL/specs/gl/glspec21.pdf * OpenGL 2.1 Specification, section 3.6.1, table 3.1 specifies that the pixelstorei alignment cannot exceed 8 */ return Math.Min(8, GetFormatSize(format)); } internal static void ValidateGetDataFormat( SurfaceFormat format, int elementSizeInBytes ) { if (GetFormatSize(format) % elementSizeInBytes != 0) { throw new ArgumentException( "The type you are using for T in this" + " method is an invalid size for this" + " resource" ); } } #endregion #region Static Mipmap Level Calculator internal static int CalculateMipLevels( int width, int height = 0, int depth = 0 ) { int levels = 1; for ( int size = Math.Max(Math.Max(width, height), depth); size > 1; levels += 1 ) { size /= 2; } return levels; } #endregion #region Static DDS Parser internal static int CalculateDDSLevelSize( int width, int height, SurfaceFormat format ) { if (format == SurfaceFormat.ColorBgraEXT) { return (((width * 32) + 7) / 8) * height; } else if (format == SurfaceFormat.HalfVector4) { return (((width * 64) + 7) / 8) * height; } else if (format == SurfaceFormat.Vector4) { return (((width * 128) + 7) / 8) * height; } else { int blockSize = 16; if (format == SurfaceFormat.Dxt1) { blockSize = 8; } width = Math.Max(width, 1); height = Math.Max(height, 1); return ( ((width + 3) / 4) * ((height + 3) / 4) * blockSize ); } } // DDS loading extension, based on MojoDDS internal static void ParseDDS( BinaryReader reader, out SurfaceFormat format, out int width, out int height, out int levels, out bool isCube ) { // A whole bunch of magic numbers, yay DDS! const uint DDS_MAGIC = 0x20534444; const uint DDS_HEADERSIZE = 124; const uint DDS_PIXFMTSIZE = 32; const uint DDSD_CAPS = 0x1; const uint DDSD_HEIGHT = 0x2; const uint DDSD_WIDTH = 0x4; const uint DDSD_PITCH = 0x8; const uint DDSD_FMT = 0x1000; const uint DDSD_LINEARSIZE = 0x80000; const uint DDSD_REQ = ( DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_FMT ); const uint DDSCAPS_MIPMAP = 0x400000; const uint DDSCAPS_TEXTURE = 0x1000; const uint DDSCAPS2_CUBEMAP = 0x200; const uint DDPF_FOURCC = 0x4; const uint DDPF_RGB = 0x40; const uint FOURCC_DXT1 = 0x31545844; const uint FOURCC_DXT3 = 0x33545844; const uint FOURCC_DXT5 = 0x35545844; const uint FOURCC_DX10 = 0x30315844; const uint pitchAndLinear = ( DDSD_PITCH | DDSD_LINEARSIZE ); // File should start with 'DDS ' if (reader.ReadUInt32() != DDS_MAGIC) { throw new NotSupportedException("Not a DDS!"); } // Texture info uint size = reader.ReadUInt32(); if (size != DDS_HEADERSIZE) { throw new NotSupportedException("Invalid DDS header!"); } uint flags = reader.ReadUInt32(); if ((flags & DDSD_REQ) != DDSD_REQ) { throw new NotSupportedException("Invalid DDS flags!"); } if ((flags & pitchAndLinear) == pitchAndLinear) { throw new NotSupportedException("Invalid DDS flags!"); } height = reader.ReadInt32(); width = reader.ReadInt32(); reader.ReadUInt32(); // dwPitchOrLinearSize, unused reader.ReadUInt32(); // dwDepth, unused levels = reader.ReadInt32(); // "Reserved" reader.ReadBytes(4 * 11); // Format info uint formatSize = reader.ReadUInt32(); if (formatSize != DDS_PIXFMTSIZE) { throw new NotSupportedException("Bogus PIXFMTSIZE!"); } uint formatFlags = reader.ReadUInt32(); uint formatFourCC = reader.ReadUInt32(); uint formatRGBBitCount = reader.ReadUInt32(); uint formatRBitMask = reader.ReadUInt32(); uint formatGBitMask = reader.ReadUInt32(); uint formatBBitMask = reader.ReadUInt32(); uint formatABitMask = reader.ReadUInt32(); // dwCaps "stuff" uint caps = reader.ReadUInt32(); if ((caps & DDSCAPS_TEXTURE) == 0) { throw new NotSupportedException("Not a texture!"); } isCube = false; uint caps2 = reader.ReadUInt32(); if (caps2 != 0) { if ((caps2 & DDSCAPS2_CUBEMAP) == DDSCAPS2_CUBEMAP) { isCube = true; } else { throw new NotSupportedException("Invalid caps2!"); } } reader.ReadUInt32(); // dwCaps3, unused reader.ReadUInt32(); // dwCaps4, unused // "Reserved" reader.ReadUInt32(); // Mipmap sanity check if ((caps & DDSCAPS_MIPMAP) != DDSCAPS_MIPMAP) { levels = 1; } // Determine texture format if ((formatFlags & DDPF_FOURCC) == DDPF_FOURCC) { switch (formatFourCC) { case 0x71: // D3DFMT_A16B16G16R16F format = SurfaceFormat.HalfVector4; break; case 0x74: // D3DFMT_A32B32G32R32F format = SurfaceFormat.Vector4; break; case FOURCC_DXT1: format = SurfaceFormat.Dxt1; break; case FOURCC_DXT3: format = SurfaceFormat.Dxt3; break; case FOURCC_DXT5: format = SurfaceFormat.Dxt5; break; case FOURCC_DX10: // If the fourCC is DX10, there is an extra header with additional format information. uint dxgiFormat = reader.ReadUInt32(); // These values are taken from the DXGI_FORMAT enum. switch (dxgiFormat) { case 2: format = SurfaceFormat.Vector4; break; case 10: format = SurfaceFormat.HalfVector4; break; case 71: format = SurfaceFormat.Dxt1; break; case 74: format = SurfaceFormat.Dxt3; break; case 77: format = SurfaceFormat.Dxt5; break; case 98: format = SurfaceFormat.Bc7EXT; break; case 99: format = SurfaceFormat.Bc7SrgbEXT; break; default: throw new NotSupportedException( "Unsupported DDS texture format" ); } uint resourceDimension = reader.ReadUInt32(); // These values are taken from the D3D10_RESOURCE_DIMENSION enum. switch (resourceDimension) { case 0: // Unknown case 1: // Buffer throw new NotSupportedException( "Unsupported DDS texture format" ); default: break; } /* * This flag seemingly only indicates if the texture is a cube map. * This is already determined above. Cool! */ reader.ReadUInt32(); /* * Indicates the number of elements in the texture array. * We don't support texture arrays so just throw if it's greater than 1. */ uint arraySize = reader.ReadUInt32(); if (arraySize > 1) { throw new NotSupportedException( "Unsupported DDS texture format" ); } reader.ReadUInt32(); // reserved break; default: throw new NotSupportedException( "Unsupported DDS texture format" ); } } else if ((formatFlags & DDPF_RGB) == DDPF_RGB) { if ( formatRGBBitCount != 32 || formatRBitMask != 0x00FF0000 || formatGBitMask != 0x0000FF00 || formatBBitMask != 0x000000FF || formatABitMask != 0xFF000000 ) { throw new NotSupportedException( "Unsupported DDS texture format" ); } format = SurfaceFormat.ColorBgraEXT; } else { throw new NotSupportedException( "Unsupported DDS texture format" ); } } #endregion } }