Beispiel #1
0
        // Note that many common DDS reader/writers (including D3DX) swap the
        // the RED/BLUE masks for 10:10:10:2 formats. We assumme
        // below that the 'backwards' header mask is being used since it is most
        // likely written by D3DX. The more robust solution is to use the 'DX10'
        // header extension and specify the Format.R10G10B10A2_UNorm format directly

        // We do not support the following legacy Direct3D 9 formats:
        //      BumpDuDv D3DFMT_V8U8, D3DFMT_Q8W8V8U8, D3DFMT_V16U16, D3DFMT_A2W10V10U10
        //      BumpLuminance D3DFMT_L6V5U5, D3DFMT_X8L8V8U8
        //      FourCC "UYVY" D3DFMT_UYVY
        //      FourCC "YUY2" D3DFMT_YUY2
        //      FourCC 117 D3DFMT_CxV8U8
        //      ZBuffer D3DFMT_D16_LOCKABLE
        //      FourCC 82 D3DFMT_D32F_LOCKABLE
        private static Format GetDXGIFormat(ref DDS.PixelFormat pixelFormat, DDSFlags flags, out ConversionFlags conversionFlags)
        {
            conversionFlags = ConversionFlags.None;

            int index = 0;

            for (index = 0; index < LegacyMaps.Length; ++index)
            {
                var entry = LegacyMaps[index];

                if ((pixelFormat.Flags & entry.PixelFormat.Flags) != 0)
                {
                    if ((entry.PixelFormat.Flags & DDS.PixelFormatFlags.FourCC) != 0)
                    {
                        if (pixelFormat.FourCC == entry.PixelFormat.FourCC)
                        {
                            break;
                        }
                    }
                    else if ((entry.PixelFormat.Flags & DDS.PixelFormatFlags.Pal8) != 0)
                    {
                        if (pixelFormat.RGBBitCount == entry.PixelFormat.RGBBitCount)
                        {
                            break;
                        }
                    }
                    else if (pixelFormat.RGBBitCount == entry.PixelFormat.RGBBitCount)
                    {
                        // RGB, RGBA, ALPHA, LUMINANCE
                        if (pixelFormat.RBitMask == entry.PixelFormat.RBitMask &&
                            pixelFormat.GBitMask == entry.PixelFormat.GBitMask &&
                            pixelFormat.BBitMask == entry.PixelFormat.BBitMask &&
                            pixelFormat.ABitMask == entry.PixelFormat.ABitMask)
                        {
                            break;
                        }
                    }
                }
            }

            if (index >= LegacyMaps.Length)
            {
                return(Format.Unknown);
            }

            conversionFlags = LegacyMaps[index].ConversionFlags;
            var format = LegacyMaps[index].Format;

            if ((conversionFlags & ConversionFlags.Expand) != 0 && (flags & DDSFlags.NoLegacyExpansion) != 0)
            {
                return(Format.Unknown);
            }

            if ((format == Format.R10G10B10A2_UNorm) && (flags & DDSFlags.NoR10B10G10A2Fixup) != 0)
            {
                conversionFlags ^= ConversionFlags.Swizzle;
            }

            return(format);
        }
Beispiel #2
0
        public void Read(GenericReader r)
        {
            var size = r.ReadUInt32();

            if (size != 124)
            {
                throw new FileFormatException($"Invalid DDS file header size: {size}.");
            }
            dwFlags = (DDSFlags)r.ReadUInt32();
            if (!Utils.ContainsBitFlags((int)dwFlags, (int)DDSFlags.Height, (int)DDSFlags.Width))
            {
                throw new FileFormatException($"Invalid DDS file flags: {dwFlags}.");
            }
            dwHeight            = r.ReadUInt32();
            dwWidth             = r.ReadUInt32();
            dwPitchOrLinearSize = r.ReadUInt32();
            dwDepth             = r.ReadUInt32();
            dwMipMapCount       = r.ReadUInt32();
            dwReserved1         = new uint[11];
            for (var i = 0; i < 11; i++)
            {
                dwReserved1[i] = r.ReadUInt32();
            }
            ddspf  = new DDSPixelFormat(r);
            dwCaps = (DDSCaps)r.ReadUInt32();
            if (!Utils.ContainsBitFlags((int)dwCaps, (int)DDSCaps.Texture))
            {
                throw new FileFormatException($"Invalid DDS file caps: {dwCaps}.");
            }
            dwCaps2     = (DDSCaps2)r.ReadUInt32();
            dwCaps3     = r.ReadUInt32();
            dwCaps4     = r.ReadUInt32();
            dwReserved2 = r.ReadUInt32();
        }
Beispiel #3
0
        private void SetTextureFormat(int mipMapCount, BitmapFormat format, BitmapFlags flags)
        {
            if (mipMapCount > 0)
            {
                MipMapCount = mipMapCount;
                Flags      |= DDSFlags.MipMapCount;
            }
            else
            {
                MipMapCount = 0;
            }

            if (flags.HasFlag(BitmapFlags.Compressed))
            {
                Flags |= DDSFlags.LinearSize;
                int blockSize      = BitmapFormatUtils.GetBlockSize(format);
                int blockDimension = BitmapFormatUtils.GetBlockDimension(format);
                var nearestWidth   = blockDimension * ((Height + (blockDimension - 1)) / blockDimension);
                var nearestHeight  = blockDimension * ((Width + (blockDimension - 1)) / blockDimension);;
                PitchOrLinearSize = (nearestWidth * nearestHeight / 16) * blockSize;
            }
            else
            {
                Flags |= DDSFlags.Pitch;
                int bitsPerPixel = BitmapFormatUtils.GetBitsPerPixel(format);
                PitchOrLinearSize = (Width * bitsPerPixel + 7) / 8;
            }

            PixelFormat = new PixelFormat(format, flags);
        }
Beispiel #4
0
        private void CreateHeaderFromType(int height, int width, int depth, int mipMapCount, BitmapFormat format, BitmapType type, BitmapFlags flags)
        {
            Height = height;
            Width  = width;

            Flags |= DDSFlags.Height | DDSFlags.Width;

            switch (type)
            {
            case BitmapType.Texture2D:
                CreateHeaderTexture2D(mipMapCount, format, flags);
                break;

            case BitmapType.Texture3D:
            case BitmapType.Array:
                CreateHeaderVolume(mipMapCount, depth, format, flags);
                break;

            case BitmapType.CubeMap:
                CreateHeaderCubemap(mipMapCount, format, flags);
                break;
            }

            return;
        }
Beispiel #5
0
        /// <summary>
        /// Read the content of the DDS header.
        /// </summary>
        /// <param name="reader"></param>
        /// <returns>File is valid</returns>
        public override bool Read(EndianReader reader)
        {
            reader.Format = EndianFormat.LittleEndian;

            var tag  = reader.ReadInt32();
            var size = reader.ReadInt32();

            if (tag != 0x20534444 && size != Size)
            {
                return(false);
            }
            else
            {
                Flags             = (DDSFlags)reader.ReadInt32();
                Height            = reader.ReadInt32();
                Width             = reader.ReadInt32();
                PitchOrLinearSize = reader.ReadInt32();
                Depth             = reader.ReadInt32();
                MipMapCount       = reader.ReadInt32();
                reader.ReadBlock(Reserved1, 0, 0x2C);
                if (!PixelFormat.Read(reader))
                {
                    return(false);
                }
                Caps      = (DDSComplexityFlags)reader.ReadInt32();
                Caps2     = (DDSSurfaceInfoFlags)reader.ReadInt32();
                Caps3     = reader.ReadInt32();
                Caps4     = reader.ReadInt32();
                Reserved2 = reader.ReadInt32();

                // Add more verifications here

                return(true);
            }
        }
Beispiel #6
0
 private void CreateHeaderVolume(int mipMapCount, int depth, BitmapFormat format, BitmapFlags flags)
 {
     Flags |= DDSFlags.Depth;
     Caps  |= DDSComplexityFlags.Complex;
     Caps2 |= DDSSurfaceInfoFlags.Volume;
     Depth  = depth;
     SetTextureFormat(mipMapCount, format, flags);
 }
Beispiel #7
0
        /// <summary>
        /// Reads a DDS file from disk. Image data is always returned as DXGI-Compliant
        /// format, therefore some old legacy formats will automatically be converted.
        /// </summary>
        /// <param name="fileName">File to load.</param>
        /// <param name="flags">Flags to control how the DDS data is loaded.</param>
        /// <returns>Loaded image data, or null if the data failed to load.</returns>
        public static DDSContainer Read(String fileName, DDSFlags flags = DDSFlags.None)
        {
            if (!File.Exists(fileName))
            {
                return(null);
            }

            using (FileStream fs = File.OpenRead(fileName))
                return(Read(fs, flags));
        }
Beispiel #8
0
        private static DDSHeader ReadHeader(BinaryReader input)
        {
            DDSHeader header = new DDSHeader();

            header.DwSize = input.ReadInt32();
            if (header.DwSize != 124)
            {
                throw new InvalidOperationException("Invalid dds header size.");
            }
            header.DwFlags         = input.ReadInt32();
            header.DwHeight        = input.ReadInt32();
            header.DwWidth         = input.ReadInt32();
            header.DwLinearSize    = input.ReadInt32();
            header.DwDepth         = input.ReadInt32();
            header.DwMipMapCount   = input.ReadInt32();
            header.DwAlphaBitDepth = input.ReadInt32();
            header.DwReserved1     = new int[10];
            for (int i = 0; i < 10; i++)
            {
                header.DwReserved1[i] = input.ReadInt32();
            }
            header.ImageFormat    = ReadImageFormat(input);
            header.DwCaps         = input.ReadInt32();
            header.DwCaps2        = input.ReadInt32();
            header.DwCaps3        = input.ReadInt32();
            header.DwCaps4        = input.ReadInt32();
            header.DwTextureStage = input.ReadInt32();

            int     mipMaps = 1 + (int)System.Math.Ceiling(System.Math.Log(System.Math.Max(header.DwHeight, header.DwWidth)) / System.Math.Log(2));
            DDSCaps cap     = (DDSCaps)header.DwCaps;

            if ((cap & DDSCaps.MipMap) == DDSCaps.MipMap)
            {
                DDSFlags flag = (DDSFlags)header.DwFlags;
                if ((flag & DDSFlags.MipCount) != DDSFlags.MipCount)
                {
                    header.DwMipMapCount = mipMaps;
                }
            }
            else
            {
                header.DwMipMapCount = 1;
            }

            return(header);
        }
Beispiel #9
0
        private void CreateHeaderTexture2D(int mipMapCount, BitmapFormat format, BitmapFlags flags)
        {
            Depth = 0;

            if (mipMapCount > 0)
            {
                MipMapCount = 1 + mipMapCount;
                Flags      |= DDSFlags.MipMapCount;
                Caps       |= DDSComplexityFlags.MipMap | DDSComplexityFlags.Complex;
            }
            else
            {
                MipMapCount = 0;
            }

            SetTextureFormat(mipMapCount, format, flags);
        }
        /// <summary>
        /// Generates a DDS Header, and if requires, a DX10 Header
        /// </summary>
        /// <param name="metaData">Meta Data</param>
        /// <param name="flags">Flags</param>
        /// <param name="header">DDS Header Output</param>
        /// <param name="dx10Header">DX10 Header Output</param>
        public static void GenerateDDSHeader(TexMetadata metaData, DDSFlags flags, out DDSHeader header, out DX10Header dx10Header)
        {
            // Check array size
            if (metaData.ArraySize > 1)
            {
                // Check if we have an array and whether we're cube maps/non-2D
                if (metaData.ArraySize != 6 || metaData.Dimension != TexDimension.TEXTURE2D || !metaData.IsCubeMap())
                {
                    // Texture1D arrays, Texture2D arrays, and Cubemap arrays must be stored using 'DX10' extended header
                    flags |= DDSFlags.FORCEDX10EXT;
                }
            }

            // Check for DX10 Ext
            if (flags.HasFlag(DDSFlags.FORCEDX10EXTMISC2))
            {
                flags |= DDSFlags.FORCEDX10EXT;
            }
            // Create DDS Header
            header = new DDSHeader
            {
                // Set Data
                Size        = (uint)Marshal.SizeOf <DDSHeader>(),
                Flags       = DDSHeader.HeaderFlags.TEXTURE,
                Caps        = (uint)DDSHeader.SurfaceFlags.TEXTURE,
                PixelFormat = new DDSHeader.DDSPixelFormat(0, 0, 0, 0, 0, 0, 0, 0)
            };
            // Create DX10 Header
            dx10Header = new DX10Header();
            // Switch format
            switch (metaData.Format)
            {
            case DXGIFormat.R8G8B8A8UNORM:
                header.PixelFormat = PixelFormats.A8B8G8R8;
                break;

            case DXGIFormat.R16G16UNORM:
                header.PixelFormat = PixelFormats.G16R16;
                break;

            case DXGIFormat.R8G8UNORM:
                header.PixelFormat = PixelFormats.A8L8;
                break;

            case DXGIFormat.R16UNORM:
                header.PixelFormat = PixelFormats.L16;
                break;

            case DXGIFormat.R8UNORM:
                header.PixelFormat = PixelFormats.L8;
                break;

            case DXGIFormat.A8UNORM:
                header.PixelFormat = PixelFormats.A8;
                break;

            case DXGIFormat.R8G8B8G8UNORM:
                header.PixelFormat = PixelFormats.R8G8B8G8;
                break;

            case DXGIFormat.G8R8G8B8UNORM:
                header.PixelFormat = PixelFormats.G8R8G8B8;
                break;

            case DXGIFormat.BC1UNORM:
                header.PixelFormat = PixelFormats.DXT1;
                break;

            case DXGIFormat.BC2UNORM:
                header.PixelFormat = metaData.IsPMAlpha() ? (PixelFormats.DXT2) : (PixelFormats.DXT3);
                break;

            case DXGIFormat.BC3UNORM:
                header.PixelFormat = metaData.IsPMAlpha() ? (PixelFormats.DXT4) : (PixelFormats.DXT5);
                break;

            case DXGIFormat.BC4UNORM:
                header.PixelFormat = PixelFormats.BC4UNORM;
                break;

            case DXGIFormat.BC4SNORM:
                header.PixelFormat = PixelFormats.BC4SNORM;
                break;

            case DXGIFormat.BC5UNORM:
                header.PixelFormat = PixelFormats.BC5UNORM;
                break;

            case DXGIFormat.BC5SNORM:
                header.PixelFormat = PixelFormats.BC5SNORM;
                break;

            case DXGIFormat.B5G6R5UNORM:
                header.PixelFormat = PixelFormats.R5G6B5;
                break;

            case DXGIFormat.B5G5R5A1UNORM:
                header.PixelFormat = PixelFormats.A1R5G5B5;
                break;

            case DXGIFormat.R8G8SNORM:
                header.PixelFormat = PixelFormats.V8U8;
                break;

            case DXGIFormat.R8G8B8A8SNORM:
                header.PixelFormat = PixelFormats.Q8W8V8U8;
                break;

            case DXGIFormat.R16G16SNORM:
                header.PixelFormat = PixelFormats.V16U16;
                break;

            case DXGIFormat.B8G8R8A8UNORM:
                header.PixelFormat = PixelFormats.A8R8G8B8;
                break;

            case DXGIFormat.B8G8R8X8UNORM:
                header.PixelFormat = PixelFormats.X8R8G8B8;
                break;

            case DXGIFormat.B4G4R4A4UNORM:
                header.PixelFormat = PixelFormats.A4R4G4B4;
                break;

            case DXGIFormat.YUY2:
                header.PixelFormat = PixelFormats.YUY2;
                break;

            // Legacy D3DX formats using D3DFMT enum value as FourCC
            case DXGIFormat.R32G32B32A32FLOAT:
                header.PixelFormat.Flags = PixelFormats.DDSFOURCC; header.PixelFormat.FourCC = 116;      // D3DFMTA32B32G32R32F
                break;

            case DXGIFormat.R16G16B16A16FLOAT:
                header.PixelFormat.Flags = PixelFormats.DDSFOURCC; header.PixelFormat.FourCC = 113;      // D3DFMTA16B16G16R16F
                break;

            case DXGIFormat.R16G16B16A16UNORM:
                header.PixelFormat.Flags = PixelFormats.DDSFOURCC; header.PixelFormat.FourCC = 36;      // D3DFMTA16B16G16R16
                break;

            case DXGIFormat.R16G16B16A16SNORM:
                header.PixelFormat.Flags = PixelFormats.DDSFOURCC; header.PixelFormat.FourCC = 110;      // D3DFMTQ16W16V16U16
                break;

            case DXGIFormat.R32G32FLOAT:
                header.PixelFormat.Flags = PixelFormats.DDSFOURCC; header.PixelFormat.FourCC = 115;      // D3DFMTG32R32F
                break;

            case DXGIFormat.R16G16FLOAT:
                header.PixelFormat.Flags = PixelFormats.DDSFOURCC; header.PixelFormat.FourCC = 112;      // D3DFMTG16R16F
                break;

            case DXGIFormat.R32FLOAT:
                header.PixelFormat.Flags = PixelFormats.DDSFOURCC; header.PixelFormat.FourCC = 114;      // D3DFMTR32F
                break;

            case DXGIFormat.R16FLOAT:
                header.PixelFormat.Flags = PixelFormats.DDSFOURCC; header.PixelFormat.FourCC = 111;      // D3DFMTR16F
                break;

            default:
                break;
            }
            // Check for mips
            if (metaData.MipLevels > 0)
            {
                // Set flag
                header.Flags |= DDSHeader.HeaderFlags.MIPMAP;
                // Check size
                if (metaData.MipLevels > UInt16.MaxValue)
                {
                    throw new ArgumentException(String.Format("Too many mipmaps: {0}. Max: {1}", metaData.MipLevels, UInt16.MaxValue));
                }
                // Set
                header.MipMapCount = (uint)metaData.MipLevels;
                // Check count
                if (header.MipMapCount > 1)
                {
                    header.Caps |= (uint)DDSHeader.SurfaceFlags.MIPMAP;
                }
            }

            // Switch Dimension
            switch (metaData.Dimension)
            {
            case TexDimension.TEXTURE1D:
            {
                // Check size
                if (metaData.Width > Int32.MaxValue)
                {
                    throw new ArgumentException(String.Format("Image Width too large: {0}. Max: {1}", metaData.Width, Int32.MaxValue));
                }
                // Set
                header.Width  = (uint)metaData.Width;
                header.Height = header.Depth = 1;
                // Check size
                break;
            }

            case TexDimension.TEXTURE2D:
            {
                // Check size
                if (metaData.Width > Int32.MaxValue || metaData.Height > Int32.MaxValue)
                {
                    throw new ArgumentException(String.Format("Image Width and/or Height too large: {0}x{1}. Max: {2}",
                                                              metaData.Width,
                                                              metaData.Height,
                                                              Int32.MaxValue));
                }
                // Set
                header.Width  = (uint)metaData.Width;
                header.Height = (uint)metaData.Height;
                header.Depth  = 1;
                // Check size
                break;
            }

            case TexDimension.TEXTURE3D:
            {
                // Check size
                if (metaData.Width > Int32.MaxValue || metaData.Height > Int32.MaxValue)
                {
                    throw new ArgumentException(String.Format("Image Width and/or Height too large: {0}x{1}. Max: {2}",
                                                              metaData.Width,
                                                              metaData.Height,
                                                              Int32.MaxValue));
                }
                // Check size
                if (metaData.Depth > UInt16.MaxValue)
                {
                    throw new ArgumentException(String.Format("Image Depth too large: {0}. Max: {1}", metaData.Depth, UInt16.MaxValue));
                }
                // Set
                header.Flags |= DDSHeader.HeaderFlags.VOLUME;
                header.Caps2 |= 0x00200000;
                header.Width  = (uint)metaData.Width;
                header.Height = (uint)metaData.Height;
                header.Depth  = (uint)metaData.Depth;
                // Check size
                break;
            }

            default:
                throw new ArgumentException("Invalid Texture Dimension.");
            }
            // Calculate the Pitch
            ComputePitch(metaData.Format, metaData.Width, metaData.Height, out long rowPitch, out long slicePitch, CPFLAGS.NONE);
            // Validate results
            if (slicePitch > UInt32.MaxValue || rowPitch > UInt32.MaxValue)
            {
                throw new ArgumentException("Failed to calculate row and/or slice pitch, values returned were too large");
            }
            // Check is it compressed
            if (IsCompressed(metaData.Format))
            {
                header.Flags            |= DDSHeader.HeaderFlags.LINEARSIZE;
                header.PitchOrLinearSize = (uint)slicePitch;
            }
            else
            {
                header.Flags            |= DDSHeader.HeaderFlags.PITCH;
                header.PitchOrLinearSize = (uint)rowPitch;
            }

            // Check for do we need to create the DX10 Header
            if (header.PixelFormat.Size == 0)
            {
                // Check size
                if (metaData.ArraySize > UInt16.MaxValue)
                {
                    throw new ArgumentException(String.Format("Array Size too large: {0}. Max: {1}", metaData.ArraySize, UInt16.MaxValue));
                }
                // Set Pixel format
                header.PixelFormat = PixelFormats.DX10;
                // Set Data
                dx10Header.Format            = metaData.Format;
                dx10Header.ResourceDimension = metaData.Dimension;
                dx10Header.MiscFlag          = metaData.MiscFlags & ~TexMiscFlags.TEXTURECUBE;
                dx10Header.ArraySize         = (uint)metaData.ArraySize;
                // Check for Cube Maps
                if (metaData.MiscFlags.HasFlag(TexMiscFlags.TEXTURECUBE))
                {
                    // Check array size, must be a multiple of 6 for cube maps
                    if ((metaData.ArraySize % 6) != 0)
                    {
                        throw new ArgumentException("Array size must be a multiple of 6");
                    }
                    // Set Flag
                    dx10Header.MiscFlag  |= TexMiscFlags.TEXTURECUBE;
                    dx10Header.ArraySize /= 6;
                }
                // Check for mist flags
                if (flags.HasFlag(DDSFlags.FORCEDX10EXTMISC2))
                {
                    // This was formerly 'reserved'. D3DX10 and D3DX11 will fail if this value is anything other than 0
                    dx10Header.MiscFlags2 = (uint)metaData.MiscFlags2;
                }
            }
        }
Beispiel #11
0
        /// <summary>
        /// Writes a DDS file to disk. Image data is expected to be 32-bit color data, if not then mipmaps are converted as necessary automatically without modifying input data.
        /// </summary>
        /// <param name="fileName">File to write to. If it doesn't exist, it will be created.</param>
        /// <param name="image">Single image to write.</param>
        /// <param name="texDim">Dimension of the texture to write.</param>
        /// <param name="flags">Flags to control how the DDS data is saved.</param>
        /// <returns>True if writing the data was successful, false if otherwise.</returns>
        public static bool Write(String fileName, Surface image, TextureDimension texDim, DDSFlags flags = DDSFlags.None)
        {
            if (!Directory.Exists(Path.GetDirectoryName(fileName)))
            {
                Directory.CreateDirectory(Path.GetDirectoryName(fileName));
            }

            using (FileStream fs = File.Create(fileName))
                return(Write(fs, image, texDim, flags));
        }
Beispiel #12
0
        /// <summary>
        /// Writes a DDS file to a stream. Image data is expected to be 32-bit color data, if not then mipmaps are converted as necessary automatically without modifying input data.
        /// </summary>
        /// <param name="output">Output stream.</param>
        /// <param name="mipChain">Single mipchain of images to write.</param>
        /// <param name="texDim">Dimension of the texture to write.</param>
        /// <param name="flags">Flags to control how the DDS data is saved.</param>
        /// <returns>True if writing the data was successful, false if otherwise.</returns>
        public static bool Write(Stream output, List <Surface> mipChain, TextureDimension texDim, DDSFlags flags = DDSFlags.None)
        {
            List <List <Surface> > mipChains = new List <List <Surface> >(1);

            mipChains.Add(mipChain);

            return(Write(output, mipChains, texDim, flags));
        }
Beispiel #13
0
        /// <summary>
        /// Writes a DDS file to a stream. Image data is expected to be 32-bit color data, if not then mipmaps are converted as necessary automatically without modifying input data.
        /// </summary>
        /// <param name="output">Output stream.</param>
        /// <param name="mipChains">Mipmap chains to write. Each mipmap chain represents a single face (so > 1 represents an array texture or a Cubemap). All faces must have
        /// equivalent dimensions and each chain must have the same number of mipmaps.</param>
        /// <param name="texDim">Dimension of the texture to write.</param>
        /// <param name="flags">Flags to control how the DDS data is saved.</param>
        /// <returns>True if writing the data was successful, false if otherwise.</returns>
        public static bool Write(Stream output, List <List <Surface> > mipChains, TextureDimension texDim, DDSFlags flags = DDSFlags.None)
        {
            if (mipChains == null || mipChains.Count == 0 || mipChains[0] == null || mipChains[0].Count == 0)
            {
                return(false);
            }

            //FreeImage doesn't support volume textures.
            if (texDim == TextureDimension.Three)
            {
                return(false);
            }

            //If texcube, must have multiples of 6, every 6 mipchains are a complete cubemap
            if (texDim == TextureDimension.Cube && (mipChains.Count % 6 != 0))
            {
                return(false);
            }

            //FreeImage surfaces are always uncompressed and we expect 32-bit color, if not we'll convert. We'll export in whatever color order freeimage is in,
            //but we can force RGBA based on the flags
            List <MipChain> ddsMipChains = new List <MipChain>(mipChains.Count);

            bool       forceRGBA     = (flags & DDSFlags.ForceRgb) == DDSFlags.ForceRgb;
            bool       isBGRAOrder   = Surface.IsBGRAOrder;
            bool       needToSwizzle = isBGRAOrder && forceRGBA;
            DXGIFormat format        = (isBGRAOrder) ? DXGIFormat.B8G8R8A8_UNorm : DXGIFormat.R8G8B8A8_UNorm;

            if (forceRGBA)
            {
                format = DXGIFormat.R8G8B8A8_UNorm;
            }

            try
            {
                int mipCount = -1;

                foreach (List <Surface> fiMipChain in mipChains)
                {
                    MipChain ddsMipChain = new MipChain(fiMipChain.Count);
                    ddsMipChains.Add(ddsMipChain);

                    if (mipCount == -1)
                    {
                        mipCount = fiMipChain.Count;
                    }

                    //All chains must have same # of mips
                    if (mipCount != fiMipChain.Count)
                    {
                        return(false);
                    }

                    foreach (Surface fiMip in fiMipChain)
                    {
                        if (fiMip == null)
                        {
                            return(false);
                        }

                        //Validate dimension
                        switch (texDim)
                        {
                        case TextureDimension.One:
                            if (fiMip.Height > 1)
                            {
                                return(false);
                            }
                            break;

                        case TextureDimension.Cube:
                            if (fiMip.Width != fiMip.Height)
                            {
                                return(false);
                            }
                            break;
                        }

                        bool is32BitBitmap = fiMip.ImageType == ImageType.Bitmap && fiMip.ColorType == ImageColorType.RGBA && fiMip.BitsPerPixel == 32;

                        if (is32BitBitmap)
                        {
                            //If no swizzling...just use the data directly
                            if (!needToSwizzle)
                            {
                                ddsMipChain.Add(new MipData(fiMip));
                            }
                            else
                            {
                                MipData newMip = new MipData(fiMip.Width, fiMip.Height, fiMip.Pitch);
                                ImageHelper.CopyColorImageData(newMip.Data, newMip.RowPitch, 0, fiMip.DataPtr, fiMip.Pitch, 0, newMip.Width, newMip.Height, 1, true);
                                ddsMipChain.Add(newMip);
                            }
                        }
                        else
                        {
                            //Need to convert. Possible to map other DXGI formats to free image bitmaps (most likely RGBA floats), but we're keeping it simple. User can wrap surfaces
                            //and use the general write method
                            using (Surface converted = fiMip.Clone())
                            {
                                if (!converted.ConvertTo(ImageConversion.To32Bits))
                                {
                                    return(false);
                                }

                                MipData newMip = new MipData(converted.Width, converted.Height, converted.Pitch);
                                ImageHelper.CopyColorImageData(newMip.Data, newMip.RowPitch, 0, converted.DataPtr, converted.Pitch, 0, newMip.Width, newMip.Height, 1, needToSwizzle);
                                ddsMipChain.Add(newMip);
                            }
                        }
                    }
                }

                //Write out DDS
                return(Write(output, ddsMipChains, format, texDim, flags));
            }
            finally
            {
                //Dispose of mip surfaces. If they own any data, it'll be cleaned up
                DisposeMipChains(ddsMipChains);
            }
        }
Beispiel #14
0
        /// <summary>
        /// Writes a DDS file to disk. Image data is expected to be DXGI-compliant data, but an effort is made to write out D3D9-compatible headers when possible.
        /// </summary>
        /// <param name="fileName">File to write to. If it doesn't exist, it will be created.</param>
        /// <param name="mipChains">Mipmap chains to write. Each mipmap chain represents a single face (so > 1 represents an array texture or a Cubemap). All faces must have
        /// equivalent dimensions and each chain must have the same number of mipmaps.</param>
        /// <param name="format">DXGI format the image data is stored as.</param>
        /// <param name="texDim">Dimension of the texture to write.</param>
        /// <param name="flags">Flags to control how the DDS data is saved.</param>
        /// <returns>True if writing the data was successful, false if otherwise.</returns>
        public static bool Write(String fileName, List <MipChain> mipChains, DXGIFormat format, TextureDimension texDim, DDSFlags flags = DDSFlags.None)
        {
            if (!Directory.Exists(Path.GetDirectoryName(fileName)))
            {
                Directory.CreateDirectory(Path.GetDirectoryName(fileName));
            }

            using (FileStream fs = File.Create(fileName))
                return(Write(fs, mipChains, format, texDim, flags));
        }
Beispiel #15
0
        /// <summary>
        /// Reads DDS formatted data from a stream. Image data is always returned as DXGI-Compliant
        /// format, therefore some old legacy formats will automatically be converted.
        /// </summary>
        /// <param name="input">Input stream.</param>
        /// <param name="flags">Flags to control how the DDS data is loaded.</param>
        /// <returns>Loaded image data, or null if the data failed to load.</returns>
        public static DDSContainer Read(Stream input, DDSFlags flags = DDSFlags.None)
        {
            StreamTransferBuffer buffer = new StreamTransferBuffer();

            Header   header;
            Header10?headerExt;

            //Reads + validates header(s)
            if (!ReadHeader(input, buffer, out header, out headerExt))
            {
                return(null);
            }

            //Gather up metadata
            List <MipChain>  mipChains   = null;
            DXGIFormat       format      = DXGIFormat.Unknown;
            TextureDimension texDim      = TextureDimension.Two;
            ConversionFlags  convFlags   = ConversionFlags.None;
            bool             legacyDword = (flags & DDSFlags.LegacyDword) == DDSFlags.LegacyDword ? true : false;

            int width      = Math.Max((int)header.Width, 1);
            int height     = Math.Max((int)header.Height, 1);
            int depth      = Math.Max((int)header.Depth, 1);
            int mipCount   = (int)header.MipMapCount;
            int arrayCount = 1;

            //Has extended header, a modern DDS
            if (headerExt.HasValue)
            {
                Header10 extendedHeader = headerExt.Value;
                arrayCount = (int)extendedHeader.ArraySize;
                format     = extendedHeader.Format;

                switch (extendedHeader.ResourceDimension)
                {
                case D3D10ResourceDimension.Texture1D:
                {
                    texDim = TextureDimension.One;

                    if (height > 1 || depth > 1)
                    {
                        return(null);
                    }
                }
                break;

                case D3D10ResourceDimension.Texture2D:
                {
                    if ((extendedHeader.MiscFlags & Header10Flags.TextureCube) == Header10Flags.TextureCube)
                    {
                        //Specifies # of cubemaps, so to get total # of faces must multiple by 6
                        arrayCount *= 6;

                        texDim = TextureDimension.Cube;
                    }
                    else
                    {
                        texDim = TextureDimension.Two;
                    }

                    if (depth > 1)
                    {
                        return(null);
                    }
                }
                break;

                case D3D10ResourceDimension.Texture3D:
                {
                    texDim = TextureDimension.Three;

                    if (arrayCount > 1 || (header.Caps2 & HeaderCaps2.Volume) != HeaderCaps2.Volume)
                    {
                        return(null);
                    }
                }
                break;
                }
            }
            else
            {
                //Otherwise, read legacy DDS and possibly convert data

                //Check volume flag
                if ((header.Caps2 & HeaderCaps2.Volume) == HeaderCaps2.Volume)
                {
                    texDim = TextureDimension.Three;
                }
                else
                {
                    //legacy DDS could not express 1D textures, so either a cubemap or a 2D non-array texture

                    if ((header.Caps2 & HeaderCaps2.Cubemap) == HeaderCaps2.Cubemap)
                    {
                        //Must have all six faces. DirectX 8 and above always would write out all 6 faces
                        if ((header.Caps2 & HeaderCaps2.Cubemap_AllFaces) != HeaderCaps2.Cubemap_AllFaces)
                        {
                            return(null);
                        }

                        arrayCount = 6;
                        texDim     = TextureDimension.Cube;
                    }
                    else
                    {
                        texDim = TextureDimension.Two;
                    }
                }

                format = FormatConverter.DetermineDXGIFormat(header.PixelFormat, flags, out convFlags);
            }

            //Modify conversion flags, if necessary
            FormatConverter.ModifyConversionFormat(ref format, ref convFlags, flags);

            //If palette image, the palette will be the first thing
            int[] palette = null;
            if (FormatConverter.HasConversionFlag(convFlags, ConversionFlags.Pal8))
            {
                palette = new int[256];
                int palSize = palette.Length * sizeof(int);
                buffer.ReadBytes(input, palSize);

                if (buffer.LastReadByteCount != palSize)
                {
                    return(null);
                }

                MemoryHelper.CopyBytes <int>(buffer.ByteArray, 0, palette, 0, palette.Length);
            }

            //Now read data based on available mip/arrays
            mipChains = new List <MipChain>(arrayCount);

            byte[] scanline     = buffer.ByteArray;
            IntPtr scanlinePtr  = buffer.Pointer;
            bool   noPadding    = (flags & DDSFlags.NoPadding) == DDSFlags.NoPadding ? true : false;
            bool   isCompressed = FormatConverter.IsCompressed(format);
            bool   errored      = false;

            try
            {
                //Iterate over each array face...
                for (int i = 0; i < arrayCount; i++)
                {
                    MipChain mipChain = new MipChain(mipCount);
                    mipChains.Add(mipChain);

                    //Iterate over each mip face...
                    for (int mipLevel = 0; mipLevel < mipCount; mipLevel++)
                    {
                        //Calculate mip dimensions
                        int mipWidth  = width;
                        int mipHeight = height;
                        int mipDepth  = depth;
                        ImageHelper.CalculateMipmapLevelDimensions(mipLevel, ref mipWidth, ref mipHeight, ref mipDepth);

                        //Compute pitch, based on MSDN programming guide which says PitchOrLinearSize is unreliable and to calculate based on format.
                        //"real" mip width/height is the given mip width/height for all non-compressed, compressed images it will be smaller since each block
                        //is a 4x4 region of pixels.
                        int realMipWidth, realMipHeight, dstRowPitch, dstSlicePitch, bytesPerPixel;
                        ImageHelper.ComputePitch(format, mipWidth, mipHeight, out dstRowPitch, out dstSlicePitch, out realMipWidth, out realMipHeight, out bytesPerPixel, legacyDword);

                        int srcRowPitch   = dstRowPitch;
                        int srcSlicePitch = dstSlicePitch;

                        //Are we converting from a legacy format, possibly?
                        if (!headerExt.HasValue)
                        {
                            int legacySize = FormatConverter.LegacyFormatBitsPerPixelFromConversionFlag(convFlags);
                            if (legacySize != 0)
                            {
                                srcRowPitch   = (realMipWidth * legacySize + 7) / 8;
                                srcSlicePitch = srcRowPitch * realMipHeight;
                            }
                        }

                        //If output data is requested not to have padding, recompute destination pitches
                        if (noPadding)
                        {
                            dstRowPitch   = bytesPerPixel * realMipWidth;
                            dstSlicePitch = dstRowPitch * realMipHeight;
                        }

                        //Setup memory to hold the loaded image
                        MipData mipSurface = new MipData(mipWidth, mipHeight, mipDepth, dstRowPitch, dstSlicePitch);
                        mipChain.Add(mipSurface);

                        //Ensure read buffer is sufficiently sized for a single scanline
                        if (buffer.Length < srcRowPitch)
                        {
                            buffer.Resize(srcRowPitch, false);
                        }

                        IntPtr dstPtr = mipSurface.Data;

                        //Advance stream one slice at a time...
                        for (int slice = 0; slice < mipDepth; slice++)
                        {
                            long   slicePos = input.Position;
                            IntPtr dPtr     = dstPtr;

                            //Copy scanline into temp buffer, do any conversions, copy to output
                            for (int row = 0; row < realMipHeight; row++)
                            {
                                int numBytesRead = input.Read(scanline, 0, srcRowPitch);
                                if (numBytesRead != srcRowPitch)
                                {
                                    errored = true;
                                    System.Diagnostics.Debug.Assert(false);
                                    return(null);
                                }

                                //Copy scanline, optionally convert data
                                FormatConverter.CopyScanline(dPtr, dstRowPitch, scanlinePtr, srcRowPitch, format, convFlags, palette);

                                //Increment dest pointer to next row
                                dPtr = MemoryHelper.AddIntPtr(dPtr, dstRowPitch);
                            }

                            //Advance stream and destination pointer to the next slice
                            input.Position = slicePos + srcSlicePitch;
                            dstPtr         = MemoryHelper.AddIntPtr(dstPtr, dstSlicePitch);
                        }
                    }
                }
            }
            finally
            {
                //If errored, clean up any mip surfaces we allocated...no null entries should have been made either
                if (errored)
                {
                    DisposeMipChains(mipChains);
                }
            }

            if (!ValidateInternal(mipChains, format, texDim))
            {
                System.Diagnostics.Debug.Assert(false);
                return(null);
            }

            return(new DDSContainer(mipChains, format, texDim));
        }
Beispiel #16
0
        public void Invoke()
        {
            string outPath = string.IsNullOrEmpty(OutputPath) ? $"{Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), Path.GetFileNameWithoutExtension(FilePath))}.dds" : $"{Path.Combine(OutputPath, Path.GetFileNameWithoutExtension(FilePath))}.dds";

            byte[] fileData = File.ReadAllBytes(FilePath);

            // I am not going to write an entire class that verifies that this is an actual uasset file so just get the magic number instead.
            if (BitConverter.ToUInt32(fileData.GetBytes(0x0, 0x4), 0x0) != 0x9E2A83C1)
            {
                Console.WriteLine("ERROR: Not a valid uasset file.");
                return;
            }

            // Check if the licensee version is FFFFFFFD (-3)
            if (BitConverter.ToUInt32(fileData.GetBytes(0x4, 0x4), 0x0) != 0xFFFFFFFD)
            {
                Console.WriteLine("ERROR: The file provided is not a Tekken 7.0 (2015) uasset file.");
                return;
            }

            Console.WriteLine("INFO: Finding PF offset...");
            int[] offsets = ArrayExtensions.Locate(fileData, Encoding.ASCII.GetBytes("PF_"));

            if (offsets.Length < 1 || offsets.Length == 0)
            {
                Console.WriteLine("ERROR: No texture was found.");
                return;
            }

            int dataStartOffset = BitConverter.ToInt32(fileData.GetBytes(0x93, 0x4), 0x0);
            int pfOffset        = offsets[1];

            Console.WriteLine("INFO: PF offset was found, getting texture info...");

            EPixelFormat pixelFormat = EPixelFormat.PF_Unknown;

            if (!Enum.TryParse(fileData.GetNullTerminatedString(pfOffset), out pixelFormat))
            {
                Console.WriteLine("ERROR: Could not retrieve texture's pixel format.");
                return;
            }

            int width  = BitConverter.ToInt32(fileData.GetBytes(pfOffset - 0x10, 0x4), 0x0);
            int height = BitConverter.ToInt32(fileData.GetBytes(pfOffset - 0xC, 0x4), 0x0);

            // NOTE TO SELF: Get rid of this awful pixel format offset calculation mess that is happening here and write something proper
            FTexture2DMipMap mipMapData = new FTexture2DMipMap(fileData.GetBytes(pfOffset + 0x10 +
                                                                                 (pixelFormat == EPixelFormat.PF_B8G8R8A8 ? 0x4 : 0x0) + // pixel format can't be BC5 and BGRA8 at the same time so it's fine
                                                                                 (pixelFormat == EPixelFormat.PF_BC5 ? -0x1 : 0x0)       // it's fine, i promise!
                                                                                 , 20));

            Console.WriteLine($"INFO: Texture resolution is {width}x{height} with the size of 0x{mipMapData.Size:X2} - Pixel Format: {pixelFormat.ToString()}");

            byte[] textureData = fileData.GetBytes(dataStartOffset, mipMapData.Size);

            Console.WriteLine("INFO: Generating a DDS file...");

            DXGIFormat dxgiFormat = FormatMapList.Find(map => map.x == pixelFormat).y;
            DDSFlags   flags      = DDSFlags.NONE;

            TexMetadata x = GenerateMataData(width, height, 0, dxgiFormat, false);
            DDSHeader   ddsHeader;
            DX10Header  dx10Header;

            dx10Header.Format = dxgiFormat;
            GenerateDDSHeader(x, flags, out ddsHeader, out dx10Header);

            List <byte> ddsData = new List <byte>();

            ddsData.AddRange(EncodeDDSHeader(ddsHeader, dx10Header));
            ddsData.AddRange(textureData);

            try
            {
                File.WriteAllBytes(outPath, ddsData.ToArray());
            }
            catch (DirectoryNotFoundException)
            {
                Directory.CreateDirectory(Path.GetDirectoryName(outPath));
                File.WriteAllBytes(outPath, ddsData.ToArray());
            }
            catch (Exception ex)
            {
                Console.WriteLine($"ERROR: {ex.Message}");
            }

            Console.WriteLine($"Extracted {Path.GetFileName(FilePath)} ({pixelFormat.ToString()}) -> {Path.GetFileName(outPath)}");
        }
Beispiel #17
0
        //-------------------------------------------------------------------------------------
        // Save a DDS to a stream
        //-------------------------------------------------------------------------------------
        public unsafe static void SaveToDDSStream(PixelBuffer[] pixelBuffers, int count, ImageDescription metadata, DDSFlags flags, System.IO.Stream stream)
        {
            // Determine memory required
            int totalSize = 0;
            int headerSize = 0;
            EncodeDDSHeader(metadata, flags, IntPtr.Zero, 0, out totalSize);
            headerSize = totalSize;

            int maxSlice = 0;

            for (int i = 0; i < pixelBuffers.Length; ++i)
            {
                int slice = pixelBuffers[i].BufferStride;
                totalSize += slice;
                if (slice > maxSlice)
                    maxSlice = slice;
            }

            Debug.Assert(totalSize > 0);

            // Allocate a single temporary buffer to save the headers and each slice.
            var buffer = new byte[Math.Max(maxSlice, headerSize)];

            fixed (void* pbuffer = buffer)
            {
                int required;
                EncodeDDSHeader(metadata, flags, (IntPtr) pbuffer, headerSize, out required);
                stream.Write(buffer, 0, headerSize);
            }

            int remaining = totalSize - headerSize;
            Debug.Assert(remaining > 0);

            int index = 0;
            for (int item = 0; item < metadata.ArraySize; ++item)
            {
                int d = metadata.Depth;

                for (int level = 0; level < metadata.MipLevels; ++level)
                {
                    for (int slice = 0; slice < d; ++slice)
                    {
                        int pixsize = pixelBuffers[index].BufferStride;
                        Utilities.Read(pixelBuffers[index].DataPointer, buffer, 0, pixsize);
                        stream.Write(buffer, 0, pixsize);
                        ++index;
                    }

                    if (d > 1)
                        d >>= 1;
                }
            }
        }
Beispiel #18
0
        private static bool WriteHeader(Stream output, StreamTransferBuffer buffer, TextureDimension texDim, DXGIFormat format, int width, int height, int depth, int arrayCount, int mipCount, DDSFlags flags)
        {
            //Force the DX10 header...
            bool writeDX10Header = (flags & DDSFlags.ForceExtendedHeader) == DDSFlags.ForceExtendedHeader;

            //Or do DX10 if the following is true...1D textures or 2D texture arrays that aren't cubemaps...
            if (!writeDX10Header)
            {
                switch (texDim)
                {
                case TextureDimension.One:
                    writeDX10Header = true;
                    break;

                case TextureDimension.Two:
                    writeDX10Header = arrayCount > 1;
                    break;
                }
            }

            //Figure out pixel format, if not writing DX10 header...
            PixelFormat pixelFormat;

            if (!writeDX10Header)
            {
                switch (format)
                {
                case DXGIFormat.R8G8B8A8_UNorm:
                    pixelFormat = PixelFormat.A8B8G8R8;
                    break;

                case DXGIFormat.R16G16_UNorm:
                    pixelFormat = PixelFormat.G16R16;
                    break;

                case DXGIFormat.R8G8_UNorm:
                    pixelFormat = PixelFormat.A8L8;
                    break;

                case DXGIFormat.R16_UNorm:
                    pixelFormat = PixelFormat.L16;
                    break;

                case DXGIFormat.R8_UNorm:
                    pixelFormat = PixelFormat.L8;
                    break;

                case DXGIFormat.A8_UNorm:
                    pixelFormat = PixelFormat.A8;
                    break;

                case DXGIFormat.R8G8_B8G8_UNorm:
                    pixelFormat = PixelFormat.R8G8_B8G8;
                    break;

                case DXGIFormat.G8R8_G8B8_UNorm:
                    pixelFormat = PixelFormat.G8R8_G8B8;
                    break;

                case DXGIFormat.BC1_UNorm:
                    pixelFormat = PixelFormat.DXT1;
                    break;

                case DXGIFormat.BC2_UNorm:
                    pixelFormat = PixelFormat.DXT3;
                    break;

                case DXGIFormat.BC3_UNorm:
                    pixelFormat = PixelFormat.DXT5;
                    break;

                case DXGIFormat.BC4_UNorm:
                    pixelFormat = PixelFormat.BC4_UNorm;
                    break;

                case DXGIFormat.BC4_SNorm:
                    pixelFormat = PixelFormat.BC4_SNorm;
                    break;

                case DXGIFormat.BC5_UNorm:
                    pixelFormat = PixelFormat.BC5_UNorm;
                    break;

                case DXGIFormat.BC5_SNorm:
                    pixelFormat = PixelFormat.BC5_SNorm;
                    break;

                case DXGIFormat.B5G6R5_UNorm:
                    pixelFormat = PixelFormat.R5G6B5;
                    break;

                case DXGIFormat.B5G5R5A1_UNorm:
                    pixelFormat = PixelFormat.A1R5G5B5;
                    break;

                case DXGIFormat.B8G8R8A8_UNorm:
                    pixelFormat = PixelFormat.A8R8G8B8;
                    break;

                case DXGIFormat.B8G8R8X8_UNorm:
                    pixelFormat = PixelFormat.X8R8G8B8;
                    break;

                case DXGIFormat.B4G4R4A4_UNorm:
                    pixelFormat = PixelFormat.A4R4G4B4;
                    break;

                case DXGIFormat.R32G32B32A32_Float:
                    pixelFormat = PixelFormat.R32G32B32A32_Float;
                    break;

                case DXGIFormat.R16G16B16A16_Float:
                    pixelFormat = PixelFormat.R16G16B16A16_Float;
                    break;

                case DXGIFormat.R16G16B16A16_UNorm:
                    pixelFormat = PixelFormat.R16G16B16A16_UNorm;
                    break;

                case DXGIFormat.R16G16B16A16_SNorm:
                    pixelFormat = PixelFormat.R16G16B16A16_SNorm;
                    break;

                case DXGIFormat.R32G32_Float:
                    pixelFormat = PixelFormat.R32G32_Float;
                    break;

                case DXGIFormat.R16G16_Float:
                    pixelFormat = PixelFormat.R16G16_Float;
                    break;

                case DXGIFormat.R32_Float:
                    pixelFormat = PixelFormat.R32_Float;
                    break;

                case DXGIFormat.R16_Float:
                    pixelFormat = PixelFormat.R16_Float;
                    break;

                default:
                    pixelFormat     = PixelFormat.DX10Extended;
                    writeDX10Header = true;
                    break;
                }
            }
            else
            {
                pixelFormat = PixelFormat.DX10Extended;
            }

            Header header = new Header();

            header.Size        = (uint)MemoryHelper.SizeOf <Header>();
            header.PixelFormat = pixelFormat;
            header.Flags       = HeaderFlags.Caps | HeaderFlags.Width | HeaderFlags.Height | HeaderFlags.PixelFormat;
            header.Caps        = HeaderCaps.Texture;

            Header10?header10 = null;

            if (mipCount > 0)
            {
                header.Flags      |= HeaderFlags.MipMapCount;
                header.MipMapCount = (uint)mipCount;
                header.Caps       |= HeaderCaps.MipMap;
            }

            switch (texDim)
            {
            case TextureDimension.One:
                header.Width  = (uint)width;
                header.Height = 1;
                header.Depth  = 1;

                //Should always be writing out extended header for 1D textures
                System.Diagnostics.Debug.Assert(writeDX10Header);

                header10 = new Header10(format, D3D10ResourceDimension.Texture1D, Header10Flags.None, (uint)arrayCount, Header10Flags2.None);

                break;

            case TextureDimension.Two:
                header.Width  = (uint)width;
                header.Height = (uint)height;
                header.Depth  = 1;

                if (writeDX10Header)
                {
                    header10 = new Header10(format, D3D10ResourceDimension.Texture2D, Header10Flags.None, (uint)arrayCount, Header10Flags2.None);
                }

                break;

            case TextureDimension.Cube:
                header.Width  = (uint)width;
                header.Height = (uint)height;
                header.Depth  = 1;
                header.Caps  |= HeaderCaps.Complex;
                header.Caps2 |= HeaderCaps2.Cubemap_AllFaces;

                //can support array tex cubes, so must be multiples of 6
                if (arrayCount % 6 != 0)
                {
                    return(false);
                }

                if (writeDX10Header)
                {
                    header10 = new Header10(format, D3D10ResourceDimension.Texture2D, Header10Flags.TextureCube, (uint)arrayCount / 6, Header10Flags2.None);
                }

                break;

            case TextureDimension.Three:
                header.Width  = (uint)width;
                header.Height = (uint)height;
                header.Depth  = (uint)depth;
                header.Flags |= HeaderFlags.Depth;
                header.Caps2 |= HeaderCaps2.Volume;

                if (arrayCount != 1)
                {
                    return(false);
                }

                if (writeDX10Header)
                {
                    header10 = new Header10(format, D3D10ResourceDimension.Texture3D, Header10Flags.None, 1, Header10Flags2.None);
                }

                break;
            }

            int realWidth, realHeight, rowPitch, slicePitch;

            ImageHelper.ComputePitch(format, width, height, out rowPitch, out slicePitch, out realWidth, out realHeight);

            if (FormatConverter.IsCompressed(format))
            {
                header.Flags            |= HeaderFlags.LinearSize;
                header.PitchOrLinearSize = (uint)slicePitch;
            }
            else
            {
                header.Flags            |= HeaderFlags.Pitch;
                header.PitchOrLinearSize = (uint)rowPitch;
            }

            //Write out magic word, DDS header, and optionally extended header
            buffer.Write <FourCC>(output, DDS_MAGIC);
            buffer.Write <Header>(output, header);

            if (header10.HasValue)
            {
                System.Diagnostics.Debug.Assert(header.PixelFormat.IsDX10Extended);
                buffer.Write <Header10>(output, header10.Value);
            }

            return(true);
        }
Beispiel #19
0
        /// <summary>
        /// Writes DDS formatted data to a stream. Image data is expected to be DXGI-compliant data, but an effort is made to write out D3D9-compatible headers when possible.
        /// </summary>
        /// <param name="output">Output stream.</param>
        /// <param name="mipChains">Mipmap chains to write. Each mipmap chain represents a single face (so > 1 represents an array texture or a Cubemap). All faces must have
        /// equivalent dimensions and each chain must have the same number of mipmaps.</param>
        /// <param name="format">DXGI format the image data is stored as.</param>
        /// <param name="texDim">Dimension of the texture to write.</param>
        /// <param name="flags">Flags to control how the DDS data is saved.</param>
        /// <returns>True if writing the data was successful, false if otherwise.</returns>
        public static bool Write(Stream output, List <MipChain> mipChains, DXGIFormat format, TextureDimension texDim, DDSFlags flags = DDSFlags.None)
        {
            if (output == null || !output.CanWrite || mipChains == null || mipChains.Count == 0 || mipChains[0].Count == 0 || format == DXGIFormat.Unknown)
            {
                return(false);
            }

            //Extract details
            int     width, height, depth, arrayCount, mipCount;
            MipData firstMip = mipChains[0][0];

            width      = firstMip.Width;
            height     = firstMip.Height;
            depth      = firstMip.Depth;
            arrayCount = mipChains.Count;
            mipCount   = mipChains[0].Count;

            if (!ValidateInternal(mipChains, format, texDim))
            {
                return(false);
            }

            //Setup a transfer buffer
            StreamTransferBuffer buffer = new StreamTransferBuffer(firstMip.RowPitch, false);

            //Write out header
            if (!WriteHeader(output, buffer, texDim, format, width, height, depth, arrayCount, mipCount, flags))
            {
                return(false);
            }

            //Iterate over each array face...
            for (int i = 0; i < arrayCount; i++)
            {
                MipChain mipChain = mipChains[i];

                //Iterate over each mip face...
                for (int mipLevel = 0; mipLevel < mipCount; mipLevel++)
                {
                    MipData mip = mipChain[mipLevel];

                    //Compute pitch, based on MSDN programming guide. We will write out these pitches rather than the supplied in order to conform to the recomendation
                    //that we compute pitch based on format
                    int realMipWidth, realMipHeight, dstRowPitch, dstSlicePitch, bytesPerPixel;
                    ImageHelper.ComputePitch(format, mip.Width, mip.Height, out dstRowPitch, out dstSlicePitch, out realMipWidth, out realMipHeight, out bytesPerPixel);

                    //Ensure write buffer is sufficiently sized for a single scanline
                    if (buffer.Length < dstRowPitch)
                    {
                        buffer.Resize(dstRowPitch, false);
                    }

                    //Sanity check
                    if (dstRowPitch < mip.RowPitch)
                    {
                        return(false);
                    }

                    IntPtr srcPtr = mip.Data;

                    //Advance stream one slice at a time...
                    for (int slice = 0; slice < mip.Depth; slice++)
                    {
                        int    bytesToWrite = dstSlicePitch;
                        IntPtr sPtr         = srcPtr;

                        //Copy scanline into temp buffer, write to output
                        for (int row = 0; row < realMipHeight; row++)
                        {
                            MemoryHelper.CopyMemory(buffer.Pointer, sPtr, dstRowPitch);
                            buffer.WriteBytes(output, dstRowPitch);
                            bytesToWrite -= dstRowPitch;

                            //Advance to next scanline in source data
                            sPtr = MemoryHelper.AddIntPtr(sPtr, mip.RowPitch);
                        }

                        //Pad slice if necessary
                        if (bytesToWrite > 0)
                        {
                            MemoryHelper.ClearMemory(buffer.Pointer, 0, bytesToWrite);
                            buffer.WriteBytes(output, bytesToWrite);
                        }

                        //Advance source pointer to next slice
                        srcPtr = MemoryHelper.AddIntPtr(srcPtr, mip.SlicePitch);
                    }
                }
            }

            return(true);
        }
Beispiel #20
0
        /// <summary>
        /// Encodes DDS file header (magic value, header, optional DX10 extended header)
        /// </summary>
        /// <param name="flags">Flags used for decoding the DDS header.</param>
        /// <param name="description">Output texture description.</param>
        /// <param name="pDestination">Pointer to the DDS output header. Can be set to IntPtr.Zero to calculated the required bytes.</param>
        /// <param name="maxsize">The maximum size of the destination buffer.</param>
        /// <param name="required">Output the number of bytes required to write the DDS header.</param>
        /// <exception cref="ArgumentException">If the argument headerPtr is null</exception>
        /// <exception cref="InvalidOperationException">If the DDS header contains invalid datas.</exception>
        /// <returns>True if the decoding is successfull, false if this is not a DDS header.</returns>
        private unsafe static void EncodeDDSHeader( ImageDescription description, DDSFlags flags,  IntPtr pDestination, int maxsize, out int required )
        {
            if (description.ArraySize > 1)
            {
                if ((description.ArraySize != 6) || (description.Dimension != TextureDimension.Texture2D) || (description.Dimension != TextureDimension.TextureCube))
                {
                    flags |= DDSFlags.ForceDX10Ext;
                }
            }

            var ddpf = default(DDS.DDSPixelFormat);
            if ((flags & DDSFlags.ForceDX10Ext) == 0)
            {
                switch (description.Format)
                {
                    case PixelFormat.R8G8B8A8_UNorm:
                        ddpf = DDS.DDSPixelFormat.A8B8G8R8;
                        break;
                    case PixelFormat.R16G16_UNorm:
                        ddpf = DDS.DDSPixelFormat.G16R16;
                        break;
                    case PixelFormat.R8G8_UNorm:
                        ddpf = DDS.DDSPixelFormat.A8L8;
                        break;
                    case PixelFormat.R16_UNorm:
                        ddpf = DDS.DDSPixelFormat.L16;
                        break;
                    case PixelFormat.R8_UNorm:
                        ddpf = DDS.DDSPixelFormat.L8;
                        break;
                    case PixelFormat.A8_UNorm:
                        ddpf = DDS.DDSPixelFormat.A8;
                        break;
                    case PixelFormat.R8G8_B8G8_UNorm:
                        ddpf = DDS.DDSPixelFormat.R8G8_B8G8;
                        break;
                    case PixelFormat.G8R8_G8B8_UNorm:
                        ddpf = DDS.DDSPixelFormat.G8R8_G8B8;
                        break;
                    case PixelFormat.BC1_UNorm:
                        ddpf = DDS.DDSPixelFormat.DXT1;
                        break;
                    case PixelFormat.BC2_UNorm:
                        ddpf = DDS.DDSPixelFormat.DXT3;
                        break;
                    case PixelFormat.BC3_UNorm:
                        ddpf = DDS.DDSPixelFormat.DXT5;
                        break;
                    case PixelFormat.BC4_UNorm:
                        ddpf = DDS.DDSPixelFormat.BC4_UNorm;
                        break;
                    case PixelFormat.BC4_SNorm:
                        ddpf = DDS.DDSPixelFormat.BC4_SNorm;
                        break;
                    case PixelFormat.BC5_UNorm:
                        ddpf = DDS.DDSPixelFormat.BC5_UNorm;
                        break;
                    case PixelFormat.BC5_SNorm:
                        ddpf = DDS.DDSPixelFormat.BC5_SNorm;
                        break;
                    case PixelFormat.B5G6R5_UNorm:
                        ddpf = DDS.DDSPixelFormat.R5G6B5;
                        break;
                    case PixelFormat.B5G5R5A1_UNorm:
                        ddpf = DDS.DDSPixelFormat.A1R5G5B5;
                        break;
                    case PixelFormat.B8G8R8A8_UNorm:
                        ddpf = DDS.DDSPixelFormat.A8R8G8B8;
                        break; // DXGI 1.1
                    case PixelFormat.B8G8R8X8_UNorm:
                        ddpf = DDS.DDSPixelFormat.X8R8G8B8;
                        break; // DXGI 1.1
#if DIRECTX11_1
                    case PixelFormat.B4G4R4A4_UNorm:
                        ddpf = DDS.PixelFormat.A4R4G4B4;
                        break;
#endif
                    // Legacy D3DX formats using D3DFMT enum value as FourCC
                    case PixelFormat.R32G32B32A32_Float:
                        ddpf.Size = Utilities.SizeOf<DDS.DDSPixelFormat>();
                        ddpf.Flags = DDS.PixelFormatFlags.FourCC;
                        ddpf.FourCC = 116; // D3DFMT_A32B32G32R32F
                        break;
                    case PixelFormat.R16G16B16A16_Float:
                        ddpf.Size = Utilities.SizeOf<DDS.DDSPixelFormat>();
                        ddpf.Flags = DDS.PixelFormatFlags.FourCC;
                        ddpf.FourCC = 113; // D3DFMT_A16B16G16R16F
                        break;
                    case PixelFormat.R16G16B16A16_UNorm:
                        ddpf.Size = Utilities.SizeOf<DDS.DDSPixelFormat>();
                        ddpf.Flags = DDS.PixelFormatFlags.FourCC;
                        ddpf.FourCC = 36; // D3DFMT_A16B16G16R16
                        break;
                    case PixelFormat.R16G16B16A16_SNorm:
                        ddpf.Size = Utilities.SizeOf<DDS.DDSPixelFormat>();
                        ddpf.Flags = DDS.PixelFormatFlags.FourCC;
                        ddpf.FourCC = 110; // D3DFMT_Q16W16V16U16
                        break;
                    case PixelFormat.R32G32_Float:
                        ddpf.Size = Utilities.SizeOf<DDS.DDSPixelFormat>();
                        ddpf.Flags = DDS.PixelFormatFlags.FourCC;
                        ddpf.FourCC = 115; // D3DFMT_G32R32F
                        break;
                    case PixelFormat.R16G16_Float:
                        ddpf.Size = Utilities.SizeOf<DDS.DDSPixelFormat>();
                        ddpf.Flags = DDS.PixelFormatFlags.FourCC;
                        ddpf.FourCC = 112; // D3DFMT_G16R16F
                        break;
                    case PixelFormat.R32_Float:
                        ddpf.Size = Utilities.SizeOf<DDS.DDSPixelFormat>();
                        ddpf.Flags = DDS.PixelFormatFlags.FourCC;
                        ddpf.FourCC = 114; // D3DFMT_R32F
                        break;
                    case PixelFormat.R16_Float:
                        ddpf.Size = Utilities.SizeOf<DDS.DDSPixelFormat>();
                        ddpf.Flags = DDS.PixelFormatFlags.FourCC;
                        ddpf.FourCC = 111; // D3DFMT_R16F
                        break;
                }
            }

            required = sizeof (int) + Utilities.SizeOf<DDS.Header>();

            if (ddpf.Size == 0)
                required += Utilities.SizeOf<DDS.HeaderDXT10>();

            if (pDestination == IntPtr.Zero)
                return;

            if (maxsize < required)
                throw new ArgumentException("Not enough size for destination buffer", "maxsize");

            *(uint*)(pDestination) = DDS.MagicHeader;

            var header = (DDS.Header*)((byte*)(pDestination) + sizeof (int));

            Utilities.ClearMemory((IntPtr)header, 0, Utilities.SizeOf<DDS.Header>());
            header->Size = Utilities.SizeOf<DDS.Header>();
            header->Flags = DDS.HeaderFlags.Texture;
            header->SurfaceFlags = DDS.SurfaceFlags.Texture;

            if (description.MipLevels > 0)
            {
                header->Flags |= DDS.HeaderFlags.Mipmap;
                header->MipMapCount = description.MipLevels;

                if (header->MipMapCount > 1)
                    header->SurfaceFlags |= DDS.SurfaceFlags.Mipmap;
            }

            switch (description.Dimension)
            {
                case TextureDimension.Texture1D:
                    header->Height = description.Height;
                    header->Width = header->Depth = 1;
                    break;

                case TextureDimension.Texture2D:
                case TextureDimension.TextureCube:
                    header->Height = description.Height;
                    header->Width = description.Width;
                    header->Depth = 1;

                    if (description.Dimension == TextureDimension.TextureCube)
                    {
                        header->SurfaceFlags |= DDS.SurfaceFlags.Cubemap;
                        header->CubemapFlags |= DDS.CubemapFlags.CubeMap | DDS.CubemapFlags.AllFaces;
                    }
                    break;

                case TextureDimension.Texture3D:

                    header->Flags |= DDS.HeaderFlags.Volume;
                    header->CubemapFlags |= DDS.CubemapFlags.Volume;
                    header->Height = description.Height;
                    header->Width = description.Width;
                    header->Depth = description.Depth;
                    break;
            }

            int rowPitch, slicePitch;
            int newWidth;
            int newHeight;
            Image.ComputePitch(description.Format, description.Width, description.Height, out rowPitch, out slicePitch, out newWidth, out newHeight);

            if (description.Format.IsCompressed())
            {
                header->Flags |= DDS.HeaderFlags.LinearSize;
                header->PitchOrLinearSize = slicePitch;
            }
            else
            {
                header->Flags |= DDS.HeaderFlags.Pitch;
                header->PitchOrLinearSize = rowPitch;
            }

            if (ddpf.Size == 0)
            {
                header->PixelFormat = DDS.DDSPixelFormat.DX10;

                var ext = (DDS.HeaderDXT10*)((byte*)(header) + Utilities.SizeOf<DDS.Header>());

                Utilities.ClearMemory((IntPtr) ext, 0, Utilities.SizeOf<DDS.HeaderDXT10>());

                ext->DXGIFormat = description.Format;
                switch (description.Dimension)
                {
                    case TextureDimension.Texture1D:
                        ext->ResourceDimension = DDS.ResourceDimension.Texture1D;
                        break;
                    case TextureDimension.Texture2D:
                    case TextureDimension.TextureCube:
                        ext->ResourceDimension = DDS.ResourceDimension.Texture2D;
                        break;
                    case TextureDimension.Texture3D:
                        ext->ResourceDimension = DDS.ResourceDimension.Texture3D;
                        break;

                }

                if (description.Dimension == TextureDimension.TextureCube)
                {
                    ext->MiscFlags |= DDS.ResourceOptionFlags.TextureCube;
                    ext->ArraySize = description.ArraySize / 6;
                }
                else
                {
                    ext->ArraySize = description.ArraySize;
                }
            }
            else
            {
                header->PixelFormat = ddpf;
            }
        }
        /// <summary>
        /// Decodes DDS header including optional DX10 extended header
        /// </summary>
        /// <param name="headerPtr">Pointer to the DDS header.</param>
        /// <param name="size">Size of the DDS content.</param>
        /// <param name="flags">Flags used for decoding the DDS header.</param>
        /// <param name="description">Output texture description.</param>
        /// <param name="convFlags">Output conversion flags.</param>
        /// <exception cref="ArgumentException">If the argument headerPtr is null</exception>
        /// <exception cref="InvalidOperationException">If the DDS header contains invalid datas.</exception>
        /// <returns>True if the decoding is successfull, false if this is not a DDS header.</returns>
        private static unsafe bool DecodeDDSHeader(IntPtr headerPtr, int size, DDSFlags flags, out ImageDescription description, out ConversionFlags convFlags)
        {
            description = new ImageDescription();
            convFlags = ConversionFlags.None;

            if (headerPtr == IntPtr.Zero)
                throw new ArgumentException("Pointer to DDS header cannot be null", "headerPtr");

            if (size < (Utilities.SizeOf<DDS.Header>() + sizeof(uint)))
                return false;

            // DDS files always start with the same magic number ("DDS ")
            if (*(uint*)(headerPtr) != DDS.MagicHeader)
                return false;

            var header = *(DDS.Header*)((byte*)headerPtr + sizeof(int));

            // Verify header to validate DDS file
            if (header.Size != Utilities.SizeOf<DDS.Header>() || header.PixelFormat.Size != Utilities.SizeOf<DDS.PixelFormat>())
                return false;

            // Setup MipLevels
            description.MipLevels = header.MipMapCount;
            if (description.MipLevels == 0)
                description.MipLevels = 1;

            // Check for DX10 extension
            if ((header.PixelFormat.Flags & DDS.PixelFormatFlags.FourCC) != 0 && (new FourCC('D', 'X', '1', '0') == header.PixelFormat.FourCC))
            {
                // Buffer must be big enough for both headers and magic value
                if (size < (Utilities.SizeOf<DDS.Header>() + sizeof(uint) + Utilities.SizeOf<DDS.HeaderDXT10>()))
                    return false;

                var headerDX10 = *(DDS.HeaderDXT10*)((byte*)headerPtr + sizeof(int) + Utilities.SizeOf<DDS.Header>());
                convFlags |= ConversionFlags.DX10;

                description.ArraySize = headerDX10.ArraySize;
                if (description.ArraySize == 0)
                    throw new InvalidOperationException("Unexpected ArraySize == 0 from DDS HeaderDX10 ");

                description.Format = headerDX10.DXGIFormat;
                if (!FormatHelper.IsValid(description.Format))
                    throw new InvalidOperationException("Invalid Format from DDS HeaderDX10 ");

                switch (headerDX10.ResourceDimension)
                {
                    case ResourceDimension.Texture1D:

                        // D3DX writes 1D textures with a fixed Height of 1
                        if ((header.Flags & DDS.HeaderFlags.Height) != 0 && header.Height != 1)
                            throw new InvalidOperationException("Unexpected Height != 1 from DDS HeaderDX10 ");

                        description.Width = header.Width;
                        description.Height = 1;
                        description.Depth = 1;
                        description.Dimension = TextureDimension.Texture1D;
                        break;

                    case ResourceDimension.Texture2D:
                        if ((headerDX10.MiscFlags & ResourceOptionFlags.TextureCube) != 0)
                        {
                            description.ArraySize *= 6;
                            description.Dimension = TextureDimension.TextureCube;
                        }
                        else
                        {
                            description.Dimension = TextureDimension.Texture2D;
                        }

                        description.Width = header.Width;
                        description.Height = header.Height;
                        description.Depth = 1;
                        break;

                    case ResourceDimension.Texture3D:
                        if ((header.Flags & DDS.HeaderFlags.Volume) == 0)
                            throw new InvalidOperationException("Texture3D missing HeaderFlags.Volume from DDS HeaderDX10");

                        if (description.ArraySize > 1)
                            throw new InvalidOperationException("Unexpected ArraySize > 1 for Texture3D from DDS HeaderDX10");

                        description.Width = header.Width;
                        description.Height = header.Height;
                        description.Depth = header.Depth;
                        description.Dimension = TextureDimension.Texture3D;
                        break;

                    default:
                        throw new InvalidOperationException(string.Format("Unexpected dimension [{0}] from DDS HeaderDX10", headerDX10.ResourceDimension));
                }
            }
            else
            {
                description.ArraySize = 1;

                if ((header.Flags & DDS.HeaderFlags.Volume) != 0)
                {
                    description.Width = header.Width;
                    description.Height = header.Height;
                    description.Depth = header.Depth;
                    description.Dimension = TextureDimension.Texture3D;
                }
                else
                {
                    if ((header.CubemapFlags & DDS.CubemapFlags.CubeMap) != 0)
                    {
                        // We require all six faces to be defined
                        if ((header.CubemapFlags & DDS.CubemapFlags.AllFaces) != DDS.CubemapFlags.AllFaces)
                            throw new InvalidOperationException("Unexpected CubeMap, expecting all faces from DDS Header");

                        description.ArraySize = 6;
                        description.Dimension = TextureDimension.TextureCube;
                    }
                    else
                    {
                        description.Dimension = TextureDimension.Texture2D;
                    }

                    description.Width = header.Width;
                    description.Height = header.Height;
                    description.Depth = 1;
                    // Note there's no way for a legacy Direct3D 9 DDS to express a '1D' texture
                }

                description.Format = GetDXGIFormat(ref header.PixelFormat, flags, out convFlags);

                if (description.Format == Format.Unknown)
                    throw new InvalidOperationException("Unsupported PixelFormat from DDS Header");
            }

            // Special flag for handling BGR DXGI 1.1 formats
            if ((flags & DDSFlags.ForceRgb) != 0)
            {
                switch ((Format)description.Format)
                {
                    case Format.B8G8R8A8_UNorm:
                        description.Format = Format.R8G8B8A8_UNorm;
                        convFlags |= ConversionFlags.Swizzle;
                        break;

                    case Format.B8G8R8X8_UNorm:
                        description.Format = Format.R8G8B8A8_UNorm;
                        convFlags |= ConversionFlags.Swizzle | ConversionFlags.NoAlpha;
                        break;

                    case Format.B8G8R8A8_Typeless:
                        description.Format = Format.R8G8B8A8_Typeless;
                        convFlags |= ConversionFlags.Swizzle;
                        break;

                    case Format.B8G8R8A8_UNorm_SRgb:
                        description.Format = Format.R8G8B8A8_UNorm_SRgb;
                        convFlags |= ConversionFlags.Swizzle;
                        break;

                    case Format.B8G8R8X8_Typeless:
                        description.Format = Format.R8G8B8A8_Typeless;
                        convFlags |= ConversionFlags.Swizzle | ConversionFlags.NoAlpha;
                        break;

                    case Format.B8G8R8X8_UNorm_SRgb:
                        description.Format = Format.R8G8B8A8_UNorm_SRgb;
                        convFlags |= ConversionFlags.Swizzle | ConversionFlags.NoAlpha;
                        break;
                }
            }

            // Pass DDSFlags copy memory to the conversion flags
            if ((flags & DDSFlags.CopyMemory) != 0)
                convFlags |= ConversionFlags.CopyMemory;

            // Special flag for handling 16bpp formats
            if ((flags & DDSFlags.No16Bpp) != 0)
            {
                switch ((Format)description.Format)
                {
                    case Format.B5G6R5_UNorm:
                    case Format.B5G5R5A1_UNorm:
                    case Format.B4G4R4A4_UNorm:
                        description.Format = Format.R8G8B8A8_UNorm;
                        convFlags |= ConversionFlags.Expand;
                        if (description.Format == Format.B5G6R5_UNorm)
                            convFlags |= ConversionFlags.NoAlpha;
                        break;
                }
            }
            return true;
        }
Beispiel #22
0
 /// <summary>
 /// Writes images to a DDS file to disk.
 /// </summary>
 /// <param name="fileName">File to write to. If it doesn't exist, it will be created.</param>
 /// <param name="flags">Flags to control how the DDS data is saved.</param>
 /// <returns>True if writing the data was successful, false if otherwise.</returns>
 public bool Write(String fileName, DDSFlags flags = DDSFlags.None)
 {
     return(DDSTypes.Write(fileName, m_mipChains, m_format, m_dimension, flags));
 }
Beispiel #23
0
 /// <summary>
 /// Writes images contained as DDS formatted data to a stream.
 /// </summary>
 /// <param name="output">Output stream.</param>
 /// <param name="flags">Flags to control how the DDS data is saved.</param>
 /// <returns>True if writing the data was successful, false if otherwise.</returns>
 public bool Write(Stream output, DDSFlags flags = DDSFlags.None)
 {
     return(DDSTypes.Write(output, m_mipChains, m_format, m_dimension, flags));
 }
        // Note that many common DDS reader/writers (including D3DX) swap the
        // the RED/BLUE masks for 10:10:10:2 formats. We assumme
        // below that the 'backwards' header mask is being used since it is most
        // likely written by D3DX. The more robust solution is to use the 'DX10'
        // header extension and specify the Format.R10G10B10A2_UNorm format directly
        // We do not support the following legacy Direct3D 9 formats:
        //      BumpDuDv D3DFMT_V8U8, D3DFMT_Q8W8V8U8, D3DFMT_V16U16, D3DFMT_A2W10V10U10
        //      BumpLuminance D3DFMT_L6V5U5, D3DFMT_X8L8V8U8
        //      FourCC "UYVY" D3DFMT_UYVY
        //      FourCC "YUY2" D3DFMT_YUY2
        //      FourCC 117 D3DFMT_CxV8U8
        //      ZBuffer D3DFMT_D16_LOCKABLE
        //      FourCC 82 D3DFMT_D32F_LOCKABLE
        private static Format GetDXGIFormat(ref DDS.PixelFormat pixelFormat, DDSFlags flags, out ConversionFlags conversionFlags)
        {
            conversionFlags = ConversionFlags.None;

            int index = 0;
            for (index = 0; index < LegacyMaps.Length; ++index)
            {
                var entry = LegacyMaps[index];

                if ((pixelFormat.Flags & entry.PixelFormat.Flags) != 0)
                {
                    if ((entry.PixelFormat.Flags & DDS.PixelFormatFlags.FourCC) != 0)
                    {
                        if (pixelFormat.FourCC == entry.PixelFormat.FourCC)
                            break;
                    }
                    else if ((entry.PixelFormat.Flags & DDS.PixelFormatFlags.Pal8) != 0)
                    {
                        if (pixelFormat.RGBBitCount == entry.PixelFormat.RGBBitCount)
                            break;
                    }
                    else if (pixelFormat.RGBBitCount == entry.PixelFormat.RGBBitCount)
                    {
                        // RGB, RGBA, ALPHA, LUMINANCE
                        if (pixelFormat.RBitMask == entry.PixelFormat.RBitMask
                            && pixelFormat.GBitMask == entry.PixelFormat.GBitMask
                            && pixelFormat.BBitMask == entry.PixelFormat.BBitMask
                            && pixelFormat.ABitMask == entry.PixelFormat.ABitMask)
                            break;
                    }
                }
            }

            if (index >= LegacyMaps.Length)
                return Format.Unknown;

            conversionFlags = LegacyMaps[index].ConversionFlags;
            var format = LegacyMaps[index].Format;

            if ((conversionFlags & ConversionFlags.Expand) != 0 && (flags & DDSFlags.NoLegacyExpansion) != 0)
                return Format.Unknown;

            if ((format == Format.R10G10B10A2_UNorm) && (flags & DDSFlags.NoR10B10G10A2Fixup) != 0)
            {
                conversionFlags ^= ConversionFlags.Swizzle;
            }

            return format;
        }
Beispiel #25
0
        /// <summary>
        /// Decodes DDS header including optional DX10 extended header
        /// </summary>
        /// <param name="headerPtr">Pointer to the DDS header.</param>
        /// <param name="size">Size of the DDS content.</param>
        /// <param name="flags">Flags used for decoding the DDS header.</param>
        /// <param name="description">Output texture description.</param>
        /// <param name="convFlags">Output conversion flags.</param>
        /// <exception cref="ArgumentException">If the argument headerPtr is null</exception>
        /// <exception cref="InvalidOperationException">If the DDS header contains invalid datas.</exception>
        /// <returns>True if the decoding is successfull, false if this is not a DDS header.</returns>
        private static unsafe bool DecodeDDSHeader(IntPtr headerPtr, int size, DDSFlags flags, out ImageDescription description, out ConversionFlags convFlags)
        {
            description = new ImageDescription();
            convFlags   = ConversionFlags.None;

            if (headerPtr == IntPtr.Zero)
            {
                throw new ArgumentException("Pointer to DDS header cannot be null", "headerPtr");
            }

            if (size < (Utilities.SizeOf <DDS.Header>() + sizeof(uint)))
            {
                return(false);
            }

            // DDS files always start with the same magic number ("DDS ")
            if (*(uint *)(headerPtr) != DDS.MagicHeader)
            {
                return(false);
            }

            var header = *(DDS.Header *)((byte *)headerPtr + sizeof(int));

            // Verify header to validate DDS file
            if (header.Size != Utilities.SizeOf <DDS.Header>() || header.PixelFormat.Size != Utilities.SizeOf <DDS.PixelFormat>())
            {
                return(false);
            }

            // Setup MipLevels
            description.MipLevels = header.MipMapCount;
            if (description.MipLevels == 0)
            {
                description.MipLevels = 1;
            }

            // Check for DX10 extension
            if ((header.PixelFormat.Flags & DDS.PixelFormatFlags.FourCC) != 0 && (new FourCC('D', 'X', '1', '0') == header.PixelFormat.FourCC))
            {
                // Buffer must be big enough for both headers and magic value
                if (size < (Utilities.SizeOf <DDS.Header>() + sizeof(uint) + Utilities.SizeOf <DDS.HeaderDXT10>()))
                {
                    return(false);
                }

                var headerDX10 = *(DDS.HeaderDXT10 *)((byte *)headerPtr + sizeof(int) + Utilities.SizeOf <DDS.Header>());
                convFlags |= ConversionFlags.DX10;

                description.ArraySize = headerDX10.ArraySize;
                if (description.ArraySize == 0)
                {
                    throw new InvalidOperationException("Unexpected ArraySize == 0 from DDS HeaderDX10 ");
                }

                description.Format = headerDX10.DXGIFormat;
                if (!FormatHelper.IsValid(description.Format))
                {
                    throw new InvalidOperationException("Invalid Format from DDS HeaderDX10 ");
                }

                switch (headerDX10.ResourceDimension)
                {
                case ResourceDimension.Texture1D:

                    // D3DX writes 1D textures with a fixed Height of 1
                    if ((header.Flags & DDS.HeaderFlags.Height) != 0 && header.Height != 1)
                    {
                        throw new InvalidOperationException("Unexpected Height != 1 from DDS HeaderDX10 ");
                    }

                    description.Width     = header.Width;
                    description.Height    = 1;
                    description.Depth     = 1;
                    description.Dimension = TextureDimension.Texture1D;
                    break;

                case ResourceDimension.Texture2D:
                    if ((headerDX10.MiscFlags & ResourceOptionFlags.TextureCube) != 0)
                    {
                        description.ArraySize *= 6;
                        description.Dimension  = TextureDimension.TextureCube;
                    }
                    else
                    {
                        description.Dimension = TextureDimension.Texture2D;
                    }

                    description.Width  = header.Width;
                    description.Height = header.Height;
                    description.Depth  = 1;
                    break;

                case ResourceDimension.Texture3D:
                    if ((header.Flags & DDS.HeaderFlags.Volume) == 0)
                    {
                        throw new InvalidOperationException("Texture3D missing HeaderFlags.Volume from DDS HeaderDX10");
                    }

                    if (description.ArraySize > 1)
                    {
                        throw new InvalidOperationException("Unexpected ArraySize > 1 for Texture3D from DDS HeaderDX10");
                    }

                    description.Width     = header.Width;
                    description.Height    = header.Height;
                    description.Depth     = header.Depth;
                    description.Dimension = TextureDimension.Texture3D;
                    break;

                default:
                    throw new InvalidOperationException(string.Format("Unexpected dimension [{0}] from DDS HeaderDX10", headerDX10.ResourceDimension));
                }
            }
            else
            {
                description.ArraySize = 1;

                if ((header.Flags & DDS.HeaderFlags.Volume) != 0)
                {
                    description.Width     = header.Width;
                    description.Height    = header.Height;
                    description.Depth     = header.Depth;
                    description.Dimension = TextureDimension.Texture3D;
                }
                else
                {
                    if ((header.CubemapFlags & DDS.CubemapFlags.CubeMap) != 0)
                    {
                        // We require all six faces to be defined
                        if ((header.CubemapFlags & DDS.CubemapFlags.AllFaces) != DDS.CubemapFlags.AllFaces)
                        {
                            throw new InvalidOperationException("Unexpected CubeMap, expecting all faces from DDS Header");
                        }

                        description.ArraySize = 6;
                        description.Dimension = TextureDimension.TextureCube;
                    }
                    else
                    {
                        description.Dimension = TextureDimension.Texture2D;
                    }

                    description.Width  = header.Width;
                    description.Height = header.Height;
                    description.Depth  = 1;
                    // Note there's no way for a legacy Direct3D 9 DDS to express a '1D' texture
                }

                description.Format = GetDXGIFormat(ref header.PixelFormat, flags, out convFlags);

                if (description.Format == Format.Unknown)
                {
                    throw new InvalidOperationException("Unsupported PixelFormat from DDS Header");
                }
            }

            // Special flag for handling BGR DXGI 1.1 formats
            if ((flags & DDSFlags.ForceRgb) != 0)
            {
                switch ((Format)description.Format)
                {
                case Format.B8G8R8A8_UNorm:
                    description.Format = Format.R8G8B8A8_UNorm;
                    convFlags         |= ConversionFlags.Swizzle;
                    break;

                case Format.B8G8R8X8_UNorm:
                    description.Format = Format.R8G8B8A8_UNorm;
                    convFlags         |= ConversionFlags.Swizzle | ConversionFlags.NoAlpha;
                    break;

                case Format.B8G8R8A8_Typeless:
                    description.Format = Format.R8G8B8A8_Typeless;
                    convFlags         |= ConversionFlags.Swizzle;
                    break;

                case Format.B8G8R8A8_UNorm_SRgb:
                    description.Format = Format.R8G8B8A8_UNorm_SRgb;
                    convFlags         |= ConversionFlags.Swizzle;
                    break;

                case Format.B8G8R8X8_Typeless:
                    description.Format = Format.R8G8B8A8_Typeless;
                    convFlags         |= ConversionFlags.Swizzle | ConversionFlags.NoAlpha;
                    break;

                case Format.B8G8R8X8_UNorm_SRgb:
                    description.Format = Format.R8G8B8A8_UNorm_SRgb;
                    convFlags         |= ConversionFlags.Swizzle | ConversionFlags.NoAlpha;
                    break;
                }
            }

            // Pass DDSFlags copy memory to the conversion flags
            if ((flags & DDSFlags.CopyMemory) != 0)
            {
                convFlags |= ConversionFlags.CopyMemory;
            }

            // Special flag for handling 16bpp formats
            if ((flags & DDSFlags.No16Bpp) != 0)
            {
                switch ((Format)description.Format)
                {
                case Format.B5G6R5_UNorm:
                case Format.B5G5R5A1_UNorm:
                case Format.B4G4R4A4_UNorm:
                    description.Format = Format.R8G8B8A8_UNorm;
                    convFlags         |= ConversionFlags.Expand;
                    if (description.Format == Format.B5G6R5_UNorm)
                    {
                        convFlags |= ConversionFlags.NoAlpha;
                    }
                    break;
                }
            }
            return(true);
        }