/// <summary> /// Checks is the given format compressed /// </summary> private static bool IsCompressed(DXGIFormat format) { switch (format) { case DXGIFormat.BC1TYPELESS: case DXGIFormat.BC1UNORM: case DXGIFormat.BC1UNORMSRGB: case DXGIFormat.BC2TYPELESS: case DXGIFormat.BC2UNORM: case DXGIFormat.BC2UNORMSRGB: case DXGIFormat.BC3TYPELESS: case DXGIFormat.BC3UNORM: case DXGIFormat.BC3UNORMSRGB: case DXGIFormat.BC4TYPELESS: case DXGIFormat.BC4UNORM: case DXGIFormat.BC4SNORM: case DXGIFormat.BC5TYPELESS: case DXGIFormat.BC5UNORM: case DXGIFormat.BC5SNORM: case DXGIFormat.BC6HTYPELESS: case DXGIFormat.BC6HUF16: case DXGIFormat.BC6HSF16: case DXGIFormat.BC7TYPELESS: case DXGIFormat.BC7UNORM: case DXGIFormat.BC7UNORMSRGB: return(true); default: return(false); } }
public byte[] GentDDSHeader(int size) { int mipCount = 0; if (HasMipmaps) { int heightMip = TextureHeight; int widthMip = TextureWidth; while (heightMip > 1 && widthMip > 1) { heightMip /= 2; widthMip /= 2; mipCount++; } mipCount--; } //Map to DXGIFormat based on XVRFormats list, recovered from a PSO1 executable. Redundancies prevent this from being an enum DXGIFormat fmt = GetFormat(); var meta = GenerateMataData(TextureWidth, TextureHeight, mipCount, fmt, false); if (UseAlpha) { meta.MiscFlags2 = TexMiscFlags2.TEXMISC2ALPHAMODEMASK; } GenerateDDSHeader(meta, DDSFlags.NONE, out var ddsHeader, out var dx10Header); return(ddsHeader.GetBytes()); }
/// <summary> /// Opens a texture to encode from a file. /// </summary> /// <param name="file">Filename of the file that contains the texture data.</param> /// <param name="pixelFormat">Pixel format to encode the texture to. If the data format does not require a pixel format, use GvrPixelFormat.Unknown.</param> /// <param name="dataFormat">Data format to encode the texture to.</param> public XvrTextureEncoder(string file, DXGIFormat pixelFormat, DXGIFormat dataFormat) : base(file) { if (decodedBitmap != null) { initalized = Initalize(pixelFormat, dataFormat); } }
private bool repackFile(TPF.Texture tpfEntry, string name, string baseDir, string subDir) { string inputPath = baseDir + "\\" + subDir + "\\" + name + ".dds"; //DBGTEX_DETAIL crashes this, what in the heck if (inputPath != null && File.Exists(inputPath) && name != "DBGTEX_DETAIL") { byte[] inputBytes = File.ReadAllBytes(inputPath); DXGIFormat originalFormat = DDSFile.Read(new MemoryStream(tpfEntry.Bytes)).Format; DXGIFormat newFormat = DDSFile.Read(new MemoryStream(inputBytes)).Format; if (originalFormat != DXGIFormat.Unknown && newFormat != DXGIFormat.Unknown && originalFormat != newFormat) { byte[] newBytes = convertFile(inputPath, originalFormat); if (newBytes != null) { inputBytes = newBytes; } } tpfEntry.Bytes = inputBytes; return(true); } else { return(false); } }
/// <summary> /// Constructs a new instance of the <see cref="DDSContainer"/> class. /// </summary> /// <param name="mipChains">Collection of mipmap chains.</param> /// <param name="format">Format of the image data.</param> /// <param name="texDim">Identifies the dimensions of the image data.</param> public DDSContainer(List <MipChain> mipChains, DXGIFormat format, TextureDimension texDim) { m_mipChains = mipChains; m_format = format; m_dimension = texDim; m_isDisposed = false; }
private void btnConvert_Click(object sender, EventArgs e) { string filepath = txtConvertFile.Text; if (File.Exists(filepath)) { if (File.Exists("bin\\texconv.exe")) { ConvertFormatItem formatItem = cmbConvertFormat.SelectedItem as ConvertFormatItem; if (formatItem == null) { return; } DXGIFormat format = formatItem.Format; filepath = Path.GetFullPath(filepath); string directory = Path.GetDirectoryName(filepath); bool backedUp = false; if (Path.GetExtension(filepath) == ".dds" && !File.Exists(filepath + ".bak")) { backedUp = true; File.Copy(filepath, filepath + ".bak"); } string args = string.Format("-f {0} -o \"{1}\" \"{2}\" -y", TPUP.PrintDXGIFormat(format), directory, filepath); ProcessStartInfo startInfo = new ProcessStartInfo("bin\\texconv.exe", args) { CreateNoWindow = true, UseShellExecute = false, RedirectStandardOutput = true }; Process texconv = Process.Start(startInfo); texconv.WaitForExit(); if (texconv.ExitCode == 0) { appendLog("Conversion successful!"); SystemSounds.Asterisk.Play(); } else { MessageBox.Show("Conversion failed.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); if (backedUp) { File.Move(filepath + ".bak", filepath); } } } else { MessageBox.Show("texconv.exe not found.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } else { MessageBox.Show("File to be converted does not exist.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } }
public Header10(DXGIFormat format, D3D10ResourceDimension resourceDim, Header10Flags miscFlags, uint arraySize, Header10Flags2 miscFlags2) { Format = format; ResourceDimension = resourceDim; MiscFlags = miscFlags; ArraySize = arraySize; MiscFlags2 = miscFlags2; }
/// <summary> /// Opens a texture to encode from a stream. /// </summary> /// <param name="source">Stream that contains the texture data.</param> /// <param name="length">Number of bytes to read.</param> /// <param name="pixelFormat">Pixel format to encode the texture to. If the data format does not require a pixel format, use GvrPixelFormat.Unknown.</param> /// <param name="dataFormat">Data format to encode the texture to.</param> public XvrTextureEncoder(Stream source, int length, DXGIFormat pixelFormat, DXGIFormat dataFormat) : base(source, length) { if (decodedBitmap != null) { initalized = Initalize(pixelFormat, dataFormat); } }
/// <summary> /// Opens a texture to encode from a bitmap. /// </summary> /// <param name="source">Bitmap to encode.</param> /// <param name="pixelFormat">Pixel format to encode the texture to. If the data format does not require a pixel format, use GvrPixelFormat.Unknown.</param> /// <param name="dataFormat">Data format to encode the texture to.</param> public XvrTextureEncoder(Bitmap source, DXGIFormat pixelFormat, DXGIFormat dataFormat) : base(source) { if (decodedBitmap != null) { initalized = Initalize(pixelFormat, dataFormat); } }
public uint miscFlags2; // see DDS_MISC_FLAGS2 public void Read(GenericReader r) { dxgiFormat = (DXGIFormat)r.ReadInt32(); resourceDimension = (DDSDimension)r.ReadUInt32(); miscFlag = r.ReadUInt32(); arraySize = r.ReadUInt32(); miscFlags2 = r.ReadUInt32(); }
/// <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)); }
public static string PrintDXGIFormat(DXGIFormat format) { if (DXGIFormatOverride.ContainsKey(format)) { return(DXGIFormatOverride[format]); } else { return(format.ToString().ToUpper()); } }
public static int GetTpfFormatFromDdsBytes(FBX2FLVERImporter importer, string texName, byte[] ddsBytes) { using (var ddsStream = new MemoryStream(ddsBytes)) { DXGIFormat format = DDSFile.Read(ddsStream).Format; switch (format) { //DSR: case DXGIFormat.BC1_UNorm: case DXGIFormat.BC1_UNorm_SRGB: return(0); case DXGIFormat.BC2_UNorm: case DXGIFormat.BC2_UNorm_SRGB: return(3); case DXGIFormat.BC3_UNorm: case DXGIFormat.BC3_UNorm_SRGB: return(5); case DXGIFormat.R16G16_Float: return(35); case DXGIFormat.BC5_UNorm: return(36); case DXGIFormat.BC6H_UF16: return(37); case DXGIFormat.BC7_UNorm: case DXGIFormat.BC7_UNorm_SRGB: return(38); //DS3: //case DXGIFormat.B5G5R5A1_UNorm: // return 6; //case DXGIFormat.B8G8R8A8_UNorm: //case DXGIFormat.B8G8R8A8_UNorm_SRGB: // return 9; //case DXGIFormat.B8G8R8X8_UNorm: //case DXGIFormat.B8G8R8X8_UNorm_SRGB: // return 10; //case DXGIFormat.R16G16B16A16_Float: // return 22; default: importer.PrintWarning($"Texture \"{texName}\" has an unrecognized" + $" DDS format type ({format.ToString()}) and will likely appear garbled ingame. " + $"For greatest compatibility, use DXT1 (aka BC1_UNorm) or DXT5 (aka BC3_UNorm) " + $"for your textures."); return(0); } } }
/// <summary> /// Gets the display modes that match the requested format and other input options. /// </summary> /// <param name="format">The color format (see <seealso cref="DXGIFormat" />).</param> /// <param name="flags"> /// Options for modes to include (see <seealso cref="DXGIEnumModes" />). <seealso cref="DXGIEnumModes.Scaling" /> needs /// to be specified /// to expose the display modes that require scaling. Centered modes, requiring no scaling and corresponding directly /// to the display output, are enumerated by default. /// </param> /// <param name="numModes"> /// Set <paramref name="modesDesc" /> to <seealso langword="null" /> so that <paramref name="numModes" /> /// returns the number of display modes that match the format and the options. /// Otherwise, <paramref name="modesDesc" /> returns the number of display modes returned in /// <paramref name="modesDesc" />. /// </param> /// <param name="modesDesc"> /// A pointer to a list of display modes (see <seealso cref="DXGIModeDescription" />); set to /// <seealso langword="null" /> to get the number of display modes. /// </param> /// <returns></returns> /// <remarks> /// In general, when switching from windowed to full-screen mode, a swap chain automatically chooses a display mode /// that meets (or exceeds) the resolution, color depth and refresh rate of the swap chain. To exercise more control /// over the display mode, use this API to poll the set of display modes that are validated against monitor /// capabilities, or all modes that match the desktop (if the desktop settings are not validated against the monitor). /// As shown, this API is designed to be called twice. First to get the number of modes available, and second to return /// a description of the modes. /// </remarks> public int GetDisplayModeList ( DXGIFormat format, DXGIEnumModes flags, ref uint numModes, [In, Out] DXGIModeDescription[] modesDesc = null ) { return(GetMethodDelegate <GetDisplayModeListDelegate>() .Invoke(this, format, flags, ref numModes, modesDesc)); }
/// <summary> /// Creates a new Texture Metadata Structe /// </summary> public TexMetadata(long width, long height, long depth, long arraySize, long mipLevels, TexMiscFlags flags, TexMiscFlags2 flags2, DXGIFormat format, TexDimension dimension) { Width = width; Height = height; Depth = depth; ArraySize = arraySize; MipLevels = mipLevels; MiscFlags = flags; MiscFlags2 = flags2; Format = format; Dimension = dimension; }
public static byte GetTpfFormatFromDdsBytes(byte[] ddsBytes) { using (var ddsStream = new MemoryStream(ddsBytes)) { DXGIFormat format = DDSFile.Read(ddsStream).Format; switch (format) { //DSR: case DXGIFormat.BC1_UNorm: case DXGIFormat.BC1_UNorm_SRGB: return(0); case DXGIFormat.BC2_UNorm: case DXGIFormat.BC2_UNorm_SRGB: return(3); case DXGIFormat.BC3_UNorm: case DXGIFormat.BC3_UNorm_SRGB: return(5); case DXGIFormat.R16G16_Float: return(35); case DXGIFormat.BC5_UNorm: return(36); case DXGIFormat.BC6H_UF16: return(37); case DXGIFormat.BC7_UNorm: case DXGIFormat.BC7_UNorm_SRGB: return(38); //DS3: //case DXGIFormat.B5G5R5A1_UNorm: // return 6; //case DXGIFormat.B8G8R8A8_UNorm: //case DXGIFormat.B8G8R8A8_UNorm_SRGB: // return 9; //case DXGIFormat.B8G8R8X8_UNorm: //case DXGIFormat.B8G8R8X8_UNorm_SRGB: // return 10; //case DXGIFormat.R16G16B16A16_Float: // return 22; default: return(0); } } }
private bool repackFile(TPF.Texture tpfEntry, string name, string baseDir, string subDir) { string inputPath = getSwappedPath(name, subDir, out bool dds); if (inputPath != null && File.Exists(inputPath)) { byte[] inputBytes = File.ReadAllBytes(inputPath); DXGIFormat originalFormat = DDSFile.Read(new MemoryStream(tpfEntry.Bytes)).Format; if (originalFormat == DXGIFormat.Unknown) { appendError("Error: {0}\r\n\u2514\u2500 Could not determine format of game file.", subDir + "\\" + name + ".dds"); } bool convert = !dds; if (dds) { DXGIFormat newFormat = DDSFile.Read(new MemoryStream(inputBytes)).Format; if (newFormat == DXGIFormat.Unknown) { appendError("Error: {0}\r\n\u2514\u2500 Could not determine format of override file.", inputPath); } if (originalFormat != DXGIFormat.Unknown && newFormat != DXGIFormat.Unknown && originalFormat != newFormat) { convert = true; } } if (convert) { byte[] newBytes = convertFile(inputPath, originalFormat); if (newBytes != null) { inputBytes = newBytes; } } tpfEntry.Bytes = inputBytes; lock (countLock) textureCount++; return(true); } else { return(false); } }
private bool Initalize(DXGIFormat pixelFormat, DXGIFormat dataFormat) { // Set the default values hasGlobalIndex = true; globalIndex = 0; useAlpha = true; useMipmaps = false; // Set the data format and pixel format and load the appropiate codecs this.dataFormat = dataFormat; this.pixelFormat = pixelFormat; return(true); }
private byte[] convertFile(string filepath, DXGIFormat format) { if (!File.Exists(TEXCONV_PATH)) { appendError("Error: texconv.exe not found."); return(null); } filepath = Path.GetFullPath(filepath); string directory = Path.GetDirectoryName(filepath); string filename = Path.GetFileName(filepath); string suffix = TEXCONV_SUFFIX + (convertInc++).ToString(); string outPath = string.Format("{0}\\{1}{2}.dds", directory, Path.GetFileNameWithoutExtension(filename), suffix); string args = string.Format("-sx {0} -f {1} -o \"{2}\" \"{2}\\{3}\" -y -singleproc -pow2", suffix, PrintDXGIFormat(format), directory, filename); ProcessStartInfo startInfo = new ProcessStartInfo(TEXCONV_PATH, args) { CreateNoWindow = true, UseShellExecute = false, RedirectStandardOutput = true }; Process texconv = Process.Start(startInfo); texconv.WaitForExit(); byte[] result = null; if (texconv.ExitCode == 0 && File.Exists(outPath)) { result = File.ReadAllBytes(outPath); try { File.Delete(outPath); } catch (Exception ex) when(ex is IOException || ex is UnauthorizedAccessException) { appendError("Error: {0}\r\n\u2514\u2500 Could not delete converted file.", outPath); } } else { appendError("Error: {0}\\{1}\r\n\u2514\u2500 Conversion failed.", directory, filename); } return(result); }
private byte[] convertFile(string filepath, DXGIFormat format) { if (!File.Exists(TEXCONV_PATH)) { return(null); } filepath = Path.GetFullPath(filepath); string directory = Path.GetDirectoryName(filepath); string filename = Path.GetFileName(filepath); string suffix = TEXCONV_SUFFIX + (convertInc++).ToString(); string outPath = string.Format("{0}\\{1}{2}.dds", directory, Path.GetFileNameWithoutExtension(filename), suffix); string args = string.Format("-sx {0} -f {1} -o \"{2}\" \"{2}\\{3}\" -y -singleproc -pow2", suffix, PrintDXGIFormat(format), directory, filename); ProcessStartInfo startInfo = new ProcessStartInfo(TEXCONV_PATH, args) { CreateNoWindow = true, UseShellExecute = false, RedirectStandardOutput = true }; Process texconv = Process.Start(startInfo); texconv.WaitForExit(); byte[] result = null; if (texconv.ExitCode == 0 && File.Exists(outPath)) { result = File.ReadAllBytes(outPath); try { File.Delete(outPath); } catch (Exception ex) { } } return(result); }
/// <summary> /// Gets the display modes that match the requested format and other input options. /// </summary> /// <param name="enumFormat">The color format (see <seealso cref="DXGIFormat" />).</param> /// <param name="flags"> /// Options for modes to include (see <seealso cref="DXGIEnumModes" />). <seealso cref="DXGIEnumModes.Scaling" /> needs /// to be specified /// to expose the display modes that require scaling. Centered modes, requiring no scaling and corresponding directly /// to the display output, are enumerated by default. /// </param> /// <param name="numModes"> /// Set <paramref name="modesDesc" /> to <seealso langword="null" /> so that <paramref name="numModes" /> /// returns the number of display modes that match the format and the options. /// Otherwise, <paramref name="modesDesc" /> returns the number of display modes returned in /// <paramref name="modesDesc" />. /// </param> /// <param name="modesDesc"> /// A pointer to a list of display modes (see <seealso cref="DXGIModeDescription1" />); set to /// <seealso langword="null" /> to get the number of display modes. /// </param> /// <returns></returns> /// <remarks> /// In general, when switching from windowed to full-screen mode, a swap chain automatically chooses a display mode /// that meets (or exceeds) the resolution, color depth and refresh rate of the swap chain. To exercise more control /// over the display mode, use this API to poll the set of display modes that are validated against monitor /// capabilities, or all modes that match the desktop (if the desktop settings are not validated against the monitor). /// As shown, this API is designed to be called twice. First to get the number of modes available, and second to return /// a description of the modes. /// </remarks> /// <returns></returns> public int GetDisplayModeList1(DXGIFormat enumFormat, DXGIEnumModes flags, ref uint numModes, DXGIModeDescription1[] modesDesc = null) { return(GetMethodDelegate <DXGIGetDisplayModeList1Delegate>() .Invoke(this, enumFormat, flags, ref numModes, modesDesc)); }
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)}"); }
internal static bool ValidateInternal(List <MipChain> mipChains, DXGIFormat format, TextureDimension texDim) { if (format == DXGIFormat.Unknown) { return(false); } //Mipchains must exist, must have at least one, and chain must have mipmaps. if (mipChains == null || mipChains.Count == 0 || mipChains[0].Count == 0) { return(false); } //Validate cubemap...must have multiples of 6 faces (can be an array of cubes). if (texDim == TextureDimension.Cube && (mipChains.Count % 6) != 0) { return(false); } //Validate 3d texture..can't have arrays if (texDim == TextureDimension.Three && mipChains.Count > 1) { return(false); } int width, height, depth, rowPitch, slicePitch; //Save the first image dimensions MipData firstSurface = mipChains[0][0]; width = firstSurface.Width; height = firstSurface.Height; depth = firstSurface.Depth; rowPitch = firstSurface.RowPitch; slicePitch = firstSurface.SlicePitch; //Validate first surface if (width < 1 || height < 1 || depth < 1 || rowPitch < 1 || slicePitch < 1) { return(false); } //Validate 1D texture...must only have 1 height if (texDim == TextureDimension.One && height > 1) { return(false); } //Validate cubemap...width/height must be same if (texDim == TextureDimension.Cube && (width != height)) { return(false); } //Only 3d textures have depth if (texDim != TextureDimension.Three && depth > 1) { return(false); } //Go through each chain and validate against the first texture and ensure mipmaps are progressively smaller int mipCount = -1; for (int i = 0; i < mipChains.Count; i++) { MipChain mipmaps = mipChains[i]; //Mips must exist... if (mipmaps == null || mipmaps.Count == 0) { return(false); } //Grab a mip count from first chain if (mipCount == -1) { mipCount = mipmaps.Count; } //Each chain must have the same number of mip surfaces if (mipmaps.Count != mipCount) { return(false); } //Each mip surface must have data and check sizes MipData prevMip = mipmaps[0]; //Check against the first main image we looked at earlier if (prevMip.Width != width || prevMip.Height != height || prevMip.Depth != depth || prevMip.Data == IntPtr.Zero || prevMip.RowPitch != rowPitch || prevMip.SlicePitch != slicePitch) { return(false); } for (int mipLevel = 1; mipLevel < mipmaps.Count; mipLevel++) { MipData nextMip = mipmaps[mipLevel]; //Ensure each mipmap is progressively smaller or same at the least if (nextMip.Width > prevMip.Width || nextMip.Height > prevMip.Height || nextMip.Depth > prevMip.Depth || nextMip.Data == IntPtr.Zero || nextMip.RowPitch > prevMip.RowPitch || nextMip.SlicePitch > prevMip.SlicePitch || nextMip.RowPitch == 0 || nextMip.SlicePitch == 0) { return(false); } prevMip = nextMip; } } return(true); }
/// <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)); }
/// <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); } }
/// <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); }
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); }
/// <summary> /// Generates DirectXTex Meta Data /// </summary> /// <param name="width">Image Width</param> /// <param name="height">Image Height</param> /// <param name="mipMapLevels">Number of Mip Maps</param> /// <param name="format">Compression Format</param> /// <param name="isCubeMap">Whether or not this is a cube map</param> /// <returns>Resulting TexMetaData Object</returns> public static TexMetadata GenerateMataData(int width, int height, int mipMapLevels, DXGIFormat format, bool isCubeMap) { // Create Texture MetaData return(new TexMetadata( width, height, 1, isCubeMap ? 6 : 1, mipMapLevels, isCubeMap ? TexMiscFlags.TEXTURECUBE : 0, 0, format, TexDimension.TEXTURE2D )); }
/// <summary> /// Computes Row and Slice Pitch /// </summary> private static void ComputePitch(DXGIFormat format, long width, long height, out long rowPitch, out long slicePitch, CPFLAGS flags) { switch (format) { case DXGIFormat.BC1TYPELESS: case DXGIFormat.BC1UNORM: case DXGIFormat.BC1UNORMSRGB: case DXGIFormat.BC4TYPELESS: case DXGIFormat.BC4UNORM: case DXGIFormat.BC4SNORM: { if (flags.HasFlag(CPFLAGS.BADDXTNTAILS)) { long nbw = width >> 2; long nbh = height >> 2; rowPitch = Clamp(1, nbw * 8, Int64.MaxValue); slicePitch = Clamp(1, rowPitch * nbh, Int64.MaxValue); } else { long nbw = Clamp(1, (width + 3) / 4, Int64.MaxValue); long nbh = Clamp(1, (height + 3) / 4, Int64.MaxValue); rowPitch = nbw * 8; slicePitch = rowPitch * nbh; } } break; case DXGIFormat.BC2TYPELESS: case DXGIFormat.BC2UNORM: case DXGIFormat.BC2UNORMSRGB: case DXGIFormat.BC3TYPELESS: case DXGIFormat.BC3UNORM: case DXGIFormat.BC3UNORMSRGB: case DXGIFormat.BC5TYPELESS: case DXGIFormat.BC5UNORM: case DXGIFormat.BC5SNORM: case DXGIFormat.BC6HTYPELESS: case DXGIFormat.BC6HUF16: case DXGIFormat.BC6HSF16: case DXGIFormat.BC7TYPELESS: case DXGIFormat.BC7UNORM: case DXGIFormat.BC7UNORMSRGB: { if (flags.HasFlag(CPFLAGS.BADDXTNTAILS)) { long nbw = width >> 2; long nbh = height >> 2; rowPitch = Clamp(1, nbw * 16, Int64.MaxValue); slicePitch = Clamp(1, rowPitch * nbh, Int64.MaxValue); } else { long nbw = Clamp(1, (width + 3) / 4, Int64.MaxValue); long nbh = Clamp(1, (height + 3) / 4, Int64.MaxValue); rowPitch = nbw * 16; slicePitch = rowPitch * nbh; } } break; case DXGIFormat.R8G8B8G8UNORM: case DXGIFormat.G8R8G8B8UNORM: case DXGIFormat.YUY2: rowPitch = ((width + 1) >> 1) * 4; slicePitch = rowPitch * height; break; case DXGIFormat.Y210: case DXGIFormat.Y216: rowPitch = ((width + 1) >> 1) * 8; slicePitch = rowPitch * height; break; case DXGIFormat.NV12: case DXGIFormat.OPAQUE420: rowPitch = ((width + 1) >> 1) * 2; slicePitch = rowPitch * (height + ((height + 1) >> 1)); break; case DXGIFormat.P010: case DXGIFormat.P016: rowPitch = ((width + 1) >> 1) * 4; slicePitch = rowPitch * (height + ((height + 1) >> 1)); break; case DXGIFormat.NV11: rowPitch = ((width + 3) >> 2) * 4; slicePitch = rowPitch * height * 2; break; default: { long bpp; if (flags.HasFlag(CPFLAGS.BPP24)) { bpp = 24; } else if (flags.HasFlag(CPFLAGS.BPP16)) { bpp = 16; } else if (flags.HasFlag(CPFLAGS.BPP8)) { bpp = 8; } else { bpp = BitsPerPixel(format); } if (flags.HasFlag(CPFLAGS.LEGACYDWORD | CPFLAGS.PARAGRAPH | CPFLAGS.YMM | CPFLAGS.ZMM | CPFLAGS.PAGE4K)) { if (flags.HasFlag(CPFLAGS.PAGE4K)) { rowPitch = ((width * bpp + 32767) / 32768) * 4096; slicePitch = rowPitch * height; } else if (flags.HasFlag(CPFLAGS.ZMM)) { rowPitch = ((width * bpp + 511) / 512) * 64; slicePitch = rowPitch * height; } else if (flags.HasFlag(CPFLAGS.YMM)) { rowPitch = ((width * bpp + 255) / 256) * 32; slicePitch = rowPitch * height; } else if (flags.HasFlag(CPFLAGS.PARAGRAPH)) { rowPitch = ((width * bpp + 127) / 128) * 16; slicePitch = rowPitch * height; } else // DWORD alignment { // Special computation for some incorrectly created DDS files based on // legacy DirectDraw assumptions about pitch alignment rowPitch = ((width * bpp + 31) / 32) * 4; slicePitch = rowPitch * height; } } else { // Default byte alignment rowPitch = (width * bpp + 7) / 8; slicePitch = rowPitch * height; } } break; } }
/// <summary> /// Gets the Bits Per Pixel for the given format /// </summary> private static long BitsPerPixel(DXGIFormat format) { switch (format) { case DXGIFormat.R32G32B32A32TYPELESS: case DXGIFormat.R32G32B32A32FLOAT: case DXGIFormat.R32G32B32A32UINT: case DXGIFormat.R32G32B32A32SINT: return(128); case DXGIFormat.R32G32B32TYPELESS: case DXGIFormat.R32G32B32FLOAT: case DXGIFormat.R32G32B32UINT: case DXGIFormat.R32G32B32SINT: return(96); case DXGIFormat.R16G16B16A16TYPELESS: case DXGIFormat.R16G16B16A16FLOAT: case DXGIFormat.R16G16B16A16UNORM: case DXGIFormat.R16G16B16A16UINT: case DXGIFormat.R16G16B16A16SNORM: case DXGIFormat.R16G16B16A16SINT: case DXGIFormat.R32G32TYPELESS: case DXGIFormat.R32G32FLOAT: case DXGIFormat.R32G32UINT: case DXGIFormat.R32G32SINT: case DXGIFormat.R32G8X24TYPELESS: case DXGIFormat.D32FLOATS8X24UINT: case DXGIFormat.R32FLOATX8X24TYPELESS: case DXGIFormat.X32TYPELESSG8X24UINT: case DXGIFormat.Y416: case DXGIFormat.Y210: case DXGIFormat.Y216: return(64); case DXGIFormat.R10G10B10A2TYPELESS: case DXGIFormat.R10G10B10A2UNORM: case DXGIFormat.R10G10B10A2UINT: case DXGIFormat.R11G11B10FLOAT: case DXGIFormat.R8G8B8A8TYPELESS: case DXGIFormat.R8G8B8A8UNORM: case DXGIFormat.R8G8B8A8UNORMSRGB: case DXGIFormat.R8G8B8A8UINT: case DXGIFormat.R8G8B8A8SNORM: case DXGIFormat.R8G8B8A8SINT: case DXGIFormat.R16G16TYPELESS: case DXGIFormat.R16G16FLOAT: case DXGIFormat.R16G16UNORM: case DXGIFormat.R16G16UINT: case DXGIFormat.R16G16SNORM: case DXGIFormat.R16G16SINT: case DXGIFormat.R32TYPELESS: case DXGIFormat.D32FLOAT: case DXGIFormat.R32FLOAT: case DXGIFormat.R32UINT: case DXGIFormat.R32SINT: case DXGIFormat.R24G8TYPELESS: case DXGIFormat.D24UNORMS8UINT: case DXGIFormat.R24UNORMX8TYPELESS: case DXGIFormat.X24TYPELESSG8UINT: case DXGIFormat.R9G9B9E5SHAREDEXP: case DXGIFormat.R8G8B8G8UNORM: case DXGIFormat.G8R8G8B8UNORM: case DXGIFormat.B8G8R8A8UNORM: case DXGIFormat.B8G8R8X8UNORM: case DXGIFormat.R10G10B10XRBIASA2UNORM: case DXGIFormat.B8G8R8A8TYPELESS: case DXGIFormat.B8G8R8A8UNORMSRGB: case DXGIFormat.B8G8R8X8TYPELESS: case DXGIFormat.B8G8R8X8UNORMSRGB: case DXGIFormat.AYUV: case DXGIFormat.Y410: case DXGIFormat.YUY2: return(32); case DXGIFormat.P010: case DXGIFormat.P016: return(24); case DXGIFormat.R8G8TYPELESS: case DXGIFormat.R8G8UNORM: case DXGIFormat.R8G8UINT: case DXGIFormat.R8G8SNORM: case DXGIFormat.R8G8SINT: case DXGIFormat.R16TYPELESS: case DXGIFormat.R16FLOAT: case DXGIFormat.D16UNORM: case DXGIFormat.R16UNORM: case DXGIFormat.R16UINT: case DXGIFormat.R16SNORM: case DXGIFormat.R16SINT: case DXGIFormat.B5G6R5UNORM: case DXGIFormat.B5G5R5A1UNORM: case DXGIFormat.A8P8: case DXGIFormat.B4G4R4A4UNORM: return(16); case DXGIFormat.NV12: case DXGIFormat.OPAQUE420: case DXGIFormat.NV11: return(12); case DXGIFormat.R8TYPELESS: case DXGIFormat.R8UNORM: case DXGIFormat.R8UINT: case DXGIFormat.R8SNORM: case DXGIFormat.R8SINT: case DXGIFormat.A8UNORM: case DXGIFormat.AI44: case DXGIFormat.IA44: case DXGIFormat.P8: return(8); case DXGIFormat.R1UNORM: return(1); case DXGIFormat.BC1TYPELESS: case DXGIFormat.BC1UNORM: case DXGIFormat.BC1UNORMSRGB: case DXGIFormat.BC4TYPELESS: case DXGIFormat.BC4UNORM: case DXGIFormat.BC4SNORM: return(4); case DXGIFormat.BC2TYPELESS: case DXGIFormat.BC2UNORM: case DXGIFormat.BC2UNORMSRGB: case DXGIFormat.BC3TYPELESS: case DXGIFormat.BC3UNORM: case DXGIFormat.BC3UNORMSRGB: case DXGIFormat.BC5TYPELESS: case DXGIFormat.BC5UNORM: case DXGIFormat.BC5SNORM: case DXGIFormat.BC6HTYPELESS: case DXGIFormat.BC6HUF16: case DXGIFormat.BC6HSF16: case DXGIFormat.BC7TYPELESS: case DXGIFormat.BC7UNORM: case DXGIFormat.BC7UNORMSRGB: return(8); default: return(0); } }