mirror of
https://mirror.ghproxy.com/https://github.com/dexyfex/CodeWalker
synced 2024-11-27 17:32:53 +08:00
2799 lines
121 KiB
C#
2799 lines
121 KiB
C#
/*
|
|
Copyright(c) 2015 Neodymium
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
THE SOFTWARE.
|
|
*/
|
|
|
|
//DDSIO: stolen and translated
|
|
|
|
|
|
|
|
|
|
// #region License
|
|
// /*
|
|
// Microsoft Public License (Ms-PL)
|
|
// MonoGame - Copyright © 2009 The MonoGame Team
|
|
//
|
|
// All rights reserved.
|
|
//
|
|
// This license governs use of the accompanying software. If you use the software, you accept this license. If you do not
|
|
// accept the license, do not use the software.
|
|
//
|
|
// 1. Definitions
|
|
// The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under
|
|
// U.S. copyright law.
|
|
//
|
|
// A "contribution" is the original software, or any additions or changes to the software.
|
|
// A "contributor" is any person that distributes its contribution under this license.
|
|
// "Licensed patents" are a contributor's patent claims that read directly on its contribution.
|
|
//
|
|
// 2. Grant of Rights
|
|
// (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3,
|
|
// each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
|
|
// (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3,
|
|
// each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
|
|
//
|
|
// 3. Conditions and Limitations
|
|
// (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
|
|
// (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software,
|
|
// your patent license from such contributor to the software ends automatically.
|
|
// (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution
|
|
// notices that are present in the software.
|
|
// (D) If you distribute any portion of the software in source code form, you may do so only under this license by including
|
|
// a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object
|
|
// code form, you may only do so under a license that complies with this license.
|
|
// (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees
|
|
// or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent
|
|
// permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular
|
|
// purpose and non-infringement.
|
|
// */
|
|
// #endregion License
|
|
|
|
//DecompressDXT: ripped from MonoGame - todo: reinterpret to remove stupid license
|
|
|
|
|
|
|
|
//additional dds importing code modified from: https://gist.github.com/spazzarama/2710d020d1d615cde20c607711655167
|
|
// DDSTextureLoader Ported to C# by Justin Stenning, March 2017
|
|
//--------------------------------------------------------------------------------------
|
|
// File: DDSTextureLoader.cpp
|
|
//
|
|
// Functions for loading a DDS texture and creating a Direct3D runtime resource for it
|
|
//
|
|
// Note these functions are useful as a light-weight runtime loader for DDS files. For
|
|
// a full-featured DDS file reader, writer, and texture processing pipeline see
|
|
// the 'Texconv' sample and the 'DirectXTex' library.
|
|
//
|
|
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
// PARTICULAR PURPOSE.
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// http://go.microsoft.com/fwlink/?LinkId=248926
|
|
// http://go.microsoft.com/fwlink/?LinkId=248929
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
using CodeWalker.GameFiles;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace CodeWalker.Utils
|
|
{
|
|
|
|
public static class DDSIO
|
|
{
|
|
|
|
|
|
public static byte[] GetPixels(Texture texture, int mip)
|
|
{
|
|
//dexyfex version
|
|
var format = GetDXGIFormat(texture.Format);
|
|
ImageStruct img = GetImageStruct(texture, format);
|
|
Image[] images = GetMipmapImages(img, format);
|
|
TexMetadata meta = GetImageMetadata(img, format);
|
|
|
|
if (meta.dimension != TEX_DIMENSION.TEX_DIMENSION_TEXTURE2D)
|
|
throw new Exception("Can only GetPixels from Texture2D.");
|
|
|
|
|
|
var i0 = images[Math.Min(Math.Max(mip, 0), images.Length - 1)];
|
|
|
|
int ddsRowPitch, ddsSlicePitch;
|
|
DXTex.ComputePitch(meta.format, i0.width, i0.height, out ddsRowPitch, out ddsSlicePitch, 0);
|
|
|
|
if (i0.slicePitch == ddsSlicePitch)
|
|
{ }
|
|
else
|
|
{ }
|
|
|
|
int w = i0.width;
|
|
int h = i0.height;
|
|
int imglen = i0.slicePitch;// h * i0.rowPitch;
|
|
byte[] imgdata = new byte[imglen];
|
|
byte[] px = null;// = new byte[w * h * 4];
|
|
|
|
if (i0.pixels + imglen > img.Data.Length)
|
|
{ throw new Exception("Mipmap not in texture!"); }
|
|
|
|
Buffer.BlockCopy(img.Data, i0.pixels, imgdata, 0, imglen);
|
|
|
|
bool swaprb = true;
|
|
|
|
switch (format)
|
|
{
|
|
// compressed
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM: // TextureFormat.D3DFMT_DXT1
|
|
px = DecompressDxt1(imgdata, w, h);
|
|
break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM: // TextureFormat.D3DFMT_DXT3
|
|
px = DecompressDxt3(imgdata, w, h);
|
|
break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM: // TextureFormat.D3DFMT_DXT5
|
|
px = DecompressDxt5(imgdata, w, h);
|
|
break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM: // TextureFormat.D3DFMT_ATI1
|
|
px = DecompressBC4(imgdata, w, h);
|
|
break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC5_UNORM: // TextureFormat.D3DFMT_ATI2
|
|
px = DecompressBC5(imgdata, w, h);
|
|
break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM: // TextureFormat.D3DFMT_BC7
|
|
//BC7 TODO!!
|
|
break;
|
|
|
|
// uncompressed
|
|
case DXGI_FORMAT.DXGI_FORMAT_B5G5R5A1_UNORM: // TextureFormat.D3DFMT_A1R5G5B5
|
|
px = ConvertBGR5A1ToRGBA8(imgdata, w, h); //needs testing
|
|
break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_A8_UNORM: // TextureFormat.D3DFMT_A8
|
|
px = ConvertA8ToRGBA8(imgdata, w, h);
|
|
swaprb = false;
|
|
break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM: // TextureFormat.D3DFMT_A8B8G8R8
|
|
px = imgdata;
|
|
break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_R8_UNORM: // TextureFormat.D3DFMT_L8
|
|
px = ConvertR8ToRGBA8(imgdata, w, h);
|
|
break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM: // TextureFormat.D3DFMT_A8R8G8B8
|
|
px = imgdata;
|
|
swaprb = false;
|
|
break;
|
|
default:
|
|
break; //shouldn't get here...
|
|
}
|
|
|
|
if (swaprb && (px != null))
|
|
{
|
|
int pxc = px.Length / 4;// w * h;
|
|
for (int i = 0; i < pxc; i++)
|
|
{
|
|
int ib = i * 4;
|
|
byte p2 = px[ib + 2];
|
|
px[ib + 2] = px[ib];
|
|
px[ib] = p2;
|
|
}
|
|
}
|
|
|
|
|
|
return px;
|
|
}
|
|
|
|
|
|
|
|
public static byte[] GetDDSFile(Texture texture)
|
|
{
|
|
var format = GetDXGIFormat(texture.Format);
|
|
ImageStruct img = GetImageStruct(texture, format);
|
|
Image[] images = GetMipmapImages(img, format);
|
|
TexMetadata meta = GetImageMetadata(img, format);
|
|
|
|
|
|
|
|
|
|
MemoryStream ms = new MemoryStream();
|
|
BinaryWriter bw = new BinaryWriter(ms);
|
|
|
|
int nimages = img.MipMapLevels;
|
|
|
|
// Create DDS Header
|
|
int required;
|
|
if (!DXTex._EncodeDDSHeader(meta, 0, bw, out required))
|
|
{
|
|
throw new Exception("Couldn't make DDS header");
|
|
}
|
|
|
|
// Write images
|
|
switch (meta.dimension)
|
|
{
|
|
case TEX_DIMENSION.TEX_DIMENSION_TEXTURE1D:
|
|
case TEX_DIMENSION.TEX_DIMENSION_TEXTURE2D:
|
|
{
|
|
int index = 0;
|
|
for (int item = 0; item < meta.arraySize; ++item)
|
|
{
|
|
for (int level = 0; level < meta.mipLevels; ++level, ++index)
|
|
{
|
|
if (index >= nimages)
|
|
throw new Exception("Tried to write mip out of range");
|
|
if (images[index].rowPitch <= 0)
|
|
throw new Exception("Invalid row pitch.");
|
|
if (images[index].slicePitch <= 0)
|
|
throw new Exception("Invalid slice pitch.");
|
|
//if (images[index].pixels)
|
|
// return E_POINTER;
|
|
|
|
int ddsRowPitch, ddsSlicePitch;
|
|
DXTex.ComputePitch(meta.format, images[index].width, images[index].height, out ddsRowPitch, out ddsSlicePitch, 0);// CP_FLAGS.CP_FLAGS_NONE);
|
|
|
|
if (images[index].slicePitch == ddsSlicePitch)
|
|
{
|
|
int lengt = ddsSlicePitch;
|
|
if (images[index].pixels + ddsSlicePitch > img.Data.Length)
|
|
{
|
|
lengt = img.Data.Length - images[index].pixels;
|
|
if (lengt <= 0)
|
|
{
|
|
//throw new Exception("Not enough data to read...");
|
|
}
|
|
}
|
|
if (lengt > 0)
|
|
{
|
|
bw.Write(img.Data, images[index].pixels, lengt);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int rowPitch = images[index].rowPitch;
|
|
if (rowPitch < ddsRowPitch)
|
|
{
|
|
// DDS uses 1-byte alignment, so if this is happening then the input pitch isn't actually a full line of data
|
|
throw new Exception("Input pitch isn't a full line of data");
|
|
}
|
|
|
|
int sPtr = images[index].pixels;
|
|
|
|
int lines = DXTex.ComputeScanlines(meta.format, images[index].height);
|
|
for (int j = 0; j < lines; ++j)
|
|
{
|
|
bw.Write(img.Data, sPtr, ddsRowPitch);
|
|
sPtr += rowPitch;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TEX_DIMENSION.TEX_DIMENSION_TEXTURE3D:
|
|
{
|
|
if (meta.arraySize != 1)
|
|
throw new Exception("Texture3D must have arraySize == 1"); //return null;// E_FAIL;
|
|
|
|
int d = meta.depth;
|
|
|
|
int index = 0;
|
|
for (int level = 0; level < meta.mipLevels; ++level)
|
|
{
|
|
for (int slice = 0; slice < d; ++slice, ++index)
|
|
{
|
|
if (index >= nimages)
|
|
throw new Exception("Tried to write mip out of range");
|
|
if (images[index].rowPitch <= 0)
|
|
throw new Exception("Invalid row pitch.");
|
|
if (images[index].slicePitch <= 0)
|
|
throw new Exception("Invalid slice pitch.");
|
|
//if (!images[index].pixels)
|
|
// return E_POINTER;
|
|
|
|
int ddsRowPitch, ddsSlicePitch;
|
|
DXTex.ComputePitch(meta.format, images[index].width, images[index].height, out ddsRowPitch, out ddsSlicePitch, 0);// CP_FLAGS_NONE);
|
|
|
|
if (images[index].slicePitch == ddsSlicePitch)
|
|
{
|
|
bw.Write(img.Data, images[index].pixels, ddsSlicePitch);
|
|
}
|
|
else
|
|
{
|
|
int rowPitch = images[index].rowPitch;
|
|
if (rowPitch < ddsRowPitch)
|
|
{
|
|
// DDS uses 1-byte alignment, so if this is happening then the input pitch isn't actually a full line of data
|
|
throw new Exception("Input pitch isn't a full line of data");
|
|
}
|
|
|
|
int sPtr = images[index].pixels;
|
|
|
|
int lines = DXTex.ComputeScanlines(meta.format, images[index].height);
|
|
for (int j = 0; j < lines; ++j)
|
|
{
|
|
bw.Write(img.Data, sPtr, ddsRowPitch);
|
|
sPtr += rowPitch;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (d > 1)
|
|
d >>= 1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
throw new Exception("Unsupported texture dimension");
|
|
}
|
|
|
|
|
|
byte[] buff = ms.GetBuffer();
|
|
byte[] outbuf = new byte[ms.Length]; //need to copy to the right size buffer for File.WriteAllBytes().
|
|
Array.Copy(buff, outbuf, outbuf.Length);
|
|
|
|
return outbuf;
|
|
}
|
|
|
|
|
|
public static Texture GetTexture(byte[] ddsfile)
|
|
{
|
|
var ms = new MemoryStream(ddsfile);
|
|
var br = new BinaryReader(ms);
|
|
|
|
var header = new DDS_HEADER();
|
|
var header10 = new DDS_HEADER_DXT10();
|
|
var useheader10 = false;
|
|
|
|
if (!DXTex._ReadDDSHeader(br, out header, out header10, out useheader10))
|
|
{ return null; }
|
|
|
|
var width = header.dwWidth;
|
|
var height = header.dwHeight;
|
|
var depth = header.dwDepth;
|
|
var mipCount = header.dwMipMapCount;
|
|
if (mipCount == 0)
|
|
{
|
|
mipCount = 1;
|
|
}
|
|
|
|
TEX_DIMENSION resDim = 0;
|
|
uint arraySize = 1;
|
|
DXGI_FORMAT format = DXGI_FORMAT.DXGI_FORMAT_UNKNOWN;
|
|
bool isCubeMap = false;
|
|
|
|
if (useheader10)
|
|
{
|
|
arraySize = header10.arraySize;
|
|
format = header10.dxgiFormat;
|
|
resDim = (TEX_DIMENSION)header10.resourceDimension;
|
|
|
|
if (arraySize == 0) throw new Exception("ArraySize was 0! This isn't supported...");
|
|
switch (format)
|
|
{
|
|
case DXGI_FORMAT.DXGI_FORMAT_AI44:
|
|
case DXGI_FORMAT.DXGI_FORMAT_IA44:
|
|
case DXGI_FORMAT.DXGI_FORMAT_P8:
|
|
case DXGI_FORMAT.DXGI_FORMAT_A8P8:
|
|
throw new NotSupportedException(string.Format("{0} DXGI format is not supported", format.ToString()));
|
|
default:
|
|
if (DXTex.BitsPerPixel(format) == 0)
|
|
throw new NotSupportedException(string.Format("{0} DXGI format is not supported", format.ToString()));
|
|
break;
|
|
}
|
|
switch (resDim)
|
|
{
|
|
case TEX_DIMENSION.TEX_DIMENSION_TEXTURE1D:
|
|
if ((header.dwFlags & DXTex.DDS_HEIGHT) > 0 && height != 1) throw new NotSupportedException("1D texture's height wasn't 1!");
|
|
height = depth = 1; // D3DX writes 1D textures with a fixed Height of 1
|
|
break;
|
|
case TEX_DIMENSION.TEX_DIMENSION_TEXTURE2D:
|
|
if ((header10.miscFlag & (uint)TEX_MISC_FLAG.TEX_MISC_TEXTURECUBE) > 0) { arraySize *= 6; isCubeMap = true; }
|
|
depth = 1;
|
|
break;
|
|
case TEX_DIMENSION.TEX_DIMENSION_TEXTURE3D:
|
|
if ((header.dwFlags & DXTex.DDS_HEADER_FLAGS_VOLUME) == 0) throw new ArgumentException("3D texture without volume flag!");
|
|
if (arraySize > 1) throw new ArgumentException("3D texture with ArraySize > 1!");
|
|
break;
|
|
default: throw new ArgumentException("Unknown resource dimension!");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
format = header.ddspf.GetDXGIFormat();
|
|
|
|
if (format == DXGI_FORMAT.DXGI_FORMAT_UNKNOWN) throw new ArgumentException("Unknown DDS format.");
|
|
if ((header.dwFlags & DXTex.DDS_HEADER_FLAGS_VOLUME) > 0)
|
|
{
|
|
resDim = TEX_DIMENSION.TEX_DIMENSION_TEXTURE3D;
|
|
}
|
|
else
|
|
{
|
|
if ((header.dwCaps2 & DXTex.DDS_CUBEMAP) > 0)
|
|
{
|
|
if ((header.dwCaps2 & DXTex.DDS_CUBEMAP_ALLFACES) != DXTex.DDS_CUBEMAP_ALLFACES) throw new ArgumentException("Not all faces in cubemap exist!");//requires all 6 faces
|
|
arraySize = 6;
|
|
isCubeMap = true;
|
|
}
|
|
depth = 1;
|
|
resDim = TEX_DIMENSION.TEX_DIMENSION_TEXTURE2D;
|
|
// Note there's no way for a legacy Direct3D 9 DDS to express a '1D' texture
|
|
}
|
|
if (DXTex.BitsPerPixel(format) == 0) throw new Exception(string.Format("{0} DXGI format is not supported", format.ToString()));
|
|
}
|
|
|
|
if (isCubeMap)
|
|
{ }
|
|
|
|
DXTex.ComputePitch(format, (int)width, (int)height, out int rowPitch, out int slicePitch, 0);
|
|
var stride = slicePitch / height;
|
|
var scanlines = DXTex.ComputeScanlines(format, (int)height);
|
|
|
|
var brem = ms.Length - ms.Position;
|
|
var ddsdata = br.ReadBytes((int)brem);
|
|
|
|
var tex = new Texture();
|
|
tex.Width = (ushort)width;
|
|
tex.Height = (ushort)height;
|
|
tex.Depth = (ushort)depth;
|
|
tex.Levels = (byte)mipCount;
|
|
tex.Format = GetTextureFormat(format);
|
|
tex.Stride = (ushort)stride;
|
|
tex.Data = new TextureData();
|
|
tex.Data.FullData = ddsdata;
|
|
|
|
//tex.Unknown_43h = (byte)header.ddspf.dwSize;
|
|
|
|
return tex;
|
|
}
|
|
|
|
|
|
|
|
|
|
private static TextureFormat GetTextureFormat(DXGI_FORMAT f)
|
|
{
|
|
var format = (TextureFormat)0;
|
|
switch (f)
|
|
{
|
|
// compressed
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM: format = TextureFormat.D3DFMT_DXT1; break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM: format = TextureFormat.D3DFMT_DXT3; break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM: format = TextureFormat.D3DFMT_DXT5; break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM: format = TextureFormat.D3DFMT_ATI1; break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC5_UNORM: format = TextureFormat.D3DFMT_ATI2; break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM: format = TextureFormat.D3DFMT_BC7; break;
|
|
|
|
// uncompressed
|
|
case DXGI_FORMAT.DXGI_FORMAT_B5G5R5A1_UNORM: format = TextureFormat.D3DFMT_A1R5G5B5; break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_A8_UNORM: format = TextureFormat.D3DFMT_A8; break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM: format = TextureFormat.D3DFMT_A8B8G8R8; break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_R8_UNORM: format = TextureFormat.D3DFMT_L8; break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM: format = TextureFormat.D3DFMT_A8R8G8B8; break;
|
|
}
|
|
return format;
|
|
}
|
|
|
|
private static DXGI_FORMAT GetDXGIFormat(TextureFormat f)
|
|
{
|
|
var format = DXGI_FORMAT.DXGI_FORMAT_UNKNOWN;
|
|
switch (f)
|
|
{
|
|
// compressed
|
|
case TextureFormat.D3DFMT_DXT1: format = DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM; break;
|
|
case TextureFormat.D3DFMT_DXT3: format = DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM; break;
|
|
case TextureFormat.D3DFMT_DXT5: format = DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM; break;
|
|
case TextureFormat.D3DFMT_ATI1: format = DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM; break;
|
|
case TextureFormat.D3DFMT_ATI2: format = DXGI_FORMAT.DXGI_FORMAT_BC5_UNORM; break;
|
|
case TextureFormat.D3DFMT_BC7: format = DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM; break;
|
|
|
|
// uncompressed
|
|
case TextureFormat.D3DFMT_A1R5G5B5: format = DXGI_FORMAT.DXGI_FORMAT_B5G5R5A1_UNORM; break;
|
|
case TextureFormat.D3DFMT_A8: format = DXGI_FORMAT.DXGI_FORMAT_A8_UNORM; break;
|
|
case TextureFormat.D3DFMT_A8B8G8R8: format = DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM; break;
|
|
case TextureFormat.D3DFMT_L8: format = DXGI_FORMAT.DXGI_FORMAT_R8_UNORM; break;
|
|
case TextureFormat.D3DFMT_A8R8G8B8: format = DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM; break;
|
|
}
|
|
return format;
|
|
}
|
|
|
|
private static ImageStruct GetImageStruct(Texture texture, DXGI_FORMAT format)
|
|
{
|
|
ImageStruct img = new ImageStruct();
|
|
img.Data = texture.Data.FullData;
|
|
img.Width = texture.Width;
|
|
img.Height = texture.Height;
|
|
img.MipMapLevels = texture.Levels;
|
|
img.Format = (int)format;
|
|
return img;
|
|
}
|
|
|
|
private static Image[] GetMipmapImages(ImageStruct img, DXGI_FORMAT format)
|
|
{
|
|
Image[] images = new Image[img.MipMapLevels];
|
|
|
|
int buf = 0;
|
|
int div = 1;
|
|
int add = 0;
|
|
for (int i = 0; i < img.MipMapLevels; i++)
|
|
{
|
|
images[i].width = img.Width / div;
|
|
images[i].height = img.Height / div;
|
|
images[i].format = format; //(DXGI_FORMAT)img.Format;
|
|
images[i].pixels = buf + add;
|
|
|
|
DXTex.ComputePitch(images[i].format, images[i].width, images[i].height, out images[i].rowPitch, out images[i].slicePitch, 0);
|
|
|
|
add += images[i].slicePitch;
|
|
div *= 2;
|
|
}
|
|
|
|
return images;
|
|
}
|
|
|
|
private static TexMetadata GetImageMetadata(ImageStruct img, DXGI_FORMAT format)
|
|
{
|
|
TexMetadata meta = new TexMetadata();
|
|
meta.width = img.Width;
|
|
meta.height = img.Height;
|
|
meta.depth = 1;
|
|
meta.arraySize = 1; // ???
|
|
meta.mipLevels = img.MipMapLevels;
|
|
meta.miscFlags = 0; // ???
|
|
meta.miscFlags2 = 0; // ???
|
|
meta.format = format; //(DXGI_FORMAT)img.Format;
|
|
meta.dimension = TEX_DIMENSION.TEX_DIMENSION_TEXTURE2D;
|
|
return meta;
|
|
}
|
|
|
|
|
|
public struct TexMetadata
|
|
{
|
|
public int width;
|
|
public int height; // Should be 1 for 1D textures
|
|
public int depth; // Should be 1 for 1D or 2D textures
|
|
public int arraySize; // For cubemap, this is a multiple of 6
|
|
public int mipLevels;
|
|
public uint miscFlags;
|
|
public uint miscFlags2;
|
|
public DXGI_FORMAT format;
|
|
public TEX_DIMENSION dimension;
|
|
|
|
// Returns size_t(-1) to indicate an out-of-range error
|
|
public int ComputeIndex(int mip, int item, int slice)
|
|
{
|
|
if (mip >= mipLevels)
|
|
return -1;
|
|
|
|
switch (dimension)
|
|
{
|
|
case TEX_DIMENSION.TEX_DIMENSION_TEXTURE1D:
|
|
case TEX_DIMENSION.TEX_DIMENSION_TEXTURE2D:
|
|
if (slice > 0)
|
|
return -1;
|
|
|
|
if (item >= arraySize)
|
|
return -1;
|
|
|
|
return (item * (mipLevels) + mip);
|
|
|
|
case TEX_DIMENSION.TEX_DIMENSION_TEXTURE3D:
|
|
if (item > 0)
|
|
{
|
|
// No support for arrays of volumes
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
int index = 0;
|
|
int d = depth;
|
|
|
|
for (int level = 0; level < mip; ++level)
|
|
{
|
|
index += d;
|
|
if (d > 1)
|
|
d >>= 1;
|
|
}
|
|
|
|
if (slice >= d)
|
|
return -1;
|
|
|
|
index += slice;
|
|
|
|
return index;
|
|
}
|
|
//break;
|
|
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// Helper for miscFlags
|
|
public bool IsCubemap()
|
|
{
|
|
return (miscFlags & (uint)TEX_MISC_FLAG.TEX_MISC_TEXTURECUBE) != 0;
|
|
}
|
|
|
|
public bool IsPMAlpha()
|
|
{
|
|
return ((miscFlags2 & (uint)TEX_MISC_FLAG.TEX_MISC2_ALPHA_MODE_MASK) == (uint)TEX_ALPHA_MODE.TEX_ALPHA_MODE_PREMULTIPLIED);
|
|
}
|
|
// Helpers for miscFlags2
|
|
//public void SetAlphaMode(TEX_ALPHA_MODE mode)
|
|
//{
|
|
// miscFlags2 = (miscFlags2 & ~TEX_MISC2_ALPHA_MODE_MASK) | static_cast<uint32_t>(mode);
|
|
//}
|
|
|
|
// Helper for dimension
|
|
public bool IsVolumemap()
|
|
{
|
|
return (dimension == TEX_DIMENSION.TEX_DIMENSION_TEXTURE3D);
|
|
}
|
|
}
|
|
|
|
public struct Image
|
|
{
|
|
public int width;
|
|
public int height;
|
|
public DXGI_FORMAT format;
|
|
public int rowPitch;
|
|
public int slicePitch;
|
|
public int pixels; //offset into data array...
|
|
};
|
|
|
|
public static class DXTex
|
|
{
|
|
public static void ComputePitch(DXGI_FORMAT fmt, int width, int height, out int rowPitch, out int slicePitch, uint flags)
|
|
{
|
|
switch (fmt)
|
|
{
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC1_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM_SRGB:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC4_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC4_SNORM:
|
|
assert(IsCompressed(fmt));
|
|
{
|
|
int nbw = Math.Max(1, (width + 3) / 4);
|
|
int nbh = Math.Max(1, (height + 3) / 4);
|
|
rowPitch = nbw * 8;
|
|
|
|
slicePitch = rowPitch * nbh;
|
|
}
|
|
break;
|
|
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC2_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM_SRGB:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC3_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM_SRGB:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC5_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC5_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC5_SNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC6H_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC6H_UF16:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC6H_SF16:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC7_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM_SRGB:
|
|
assert(IsCompressed(fmt));
|
|
{
|
|
int nbw = Math.Max(1, (width + 3) / 4);
|
|
int nbh = Math.Max(1, (height + 3) / 4);
|
|
rowPitch = nbw * 16;
|
|
|
|
slicePitch = rowPitch * nbh;
|
|
}
|
|
break;
|
|
|
|
case DXGI_FORMAT.DXGI_FORMAT_R8G8_B8G8_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_G8R8_G8B8_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_YUY2:
|
|
assert(IsPacked(fmt));
|
|
rowPitch = ((width + 1) >> 1) * 4;
|
|
slicePitch = rowPitch * height;
|
|
break;
|
|
|
|
case DXGI_FORMAT.DXGI_FORMAT_Y210:
|
|
case DXGI_FORMAT.DXGI_FORMAT_Y216:
|
|
assert(IsPacked(fmt));
|
|
rowPitch = ((width + 1) >> 1) * 8;
|
|
slicePitch = rowPitch * height;
|
|
break;
|
|
|
|
case DXGI_FORMAT.DXGI_FORMAT_NV12:
|
|
case DXGI_FORMAT.DXGI_FORMAT_420_OPAQUE:
|
|
assert(IsPlanar(fmt));
|
|
rowPitch = ((width + 1) >> 1) * 2;
|
|
slicePitch = rowPitch * (height + ((height + 1) >> 1));
|
|
break;
|
|
|
|
case DXGI_FORMAT.DXGI_FORMAT_P010:
|
|
case DXGI_FORMAT.DXGI_FORMAT_P016:
|
|
case DXGI_FORMAT.XBOX_DXGI_FORMAT_D16_UNORM_S8_UINT:
|
|
case DXGI_FORMAT.XBOX_DXGI_FORMAT_R16_UNORM_X8_TYPELESS:
|
|
case DXGI_FORMAT.XBOX_DXGI_FORMAT_X16_TYPELESS_G8_UINT:
|
|
assert(IsPlanar(fmt));
|
|
rowPitch = ((width + 1) >> 1) * 4;
|
|
slicePitch = rowPitch * (height + ((height + 1) >> 1));
|
|
break;
|
|
|
|
case DXGI_FORMAT.DXGI_FORMAT_NV11:
|
|
assert(IsPlanar(fmt));
|
|
rowPitch = ((width + 3) >> 2) * 4;
|
|
slicePitch = rowPitch * height * 2;
|
|
break;
|
|
|
|
case DXGI_FORMAT.WIN10_DXGI_FORMAT_P208:
|
|
assert(IsPlanar(fmt));
|
|
rowPitch = ((width + 1) >> 1) * 2;
|
|
slicePitch = rowPitch * height * 2;
|
|
break;
|
|
|
|
case DXGI_FORMAT.WIN10_DXGI_FORMAT_V208:
|
|
assert(IsPlanar(fmt));
|
|
rowPitch = width;
|
|
slicePitch = rowPitch * (height + (((height + 1) >> 1) * 2));
|
|
break;
|
|
|
|
case DXGI_FORMAT.WIN10_DXGI_FORMAT_V408:
|
|
assert(IsPlanar(fmt));
|
|
rowPitch = width;
|
|
slicePitch = rowPitch * (height + ((height >> 1) * 4));
|
|
break;
|
|
|
|
default:
|
|
assert(IsValid(fmt));
|
|
assert(!IsCompressed(fmt) && !IsPacked(fmt) && !IsPlanar(fmt));
|
|
{
|
|
|
|
int bpp;
|
|
|
|
if ((flags & (uint)CP_FLAGS.CP_FLAGS_24BPP) != 0)
|
|
bpp = 24;
|
|
else if ((flags & (uint)CP_FLAGS.CP_FLAGS_16BPP) != 0)
|
|
bpp = 16;
|
|
else if ((flags & (uint)CP_FLAGS.CP_FLAGS_8BPP) != 0)
|
|
bpp = 8;
|
|
else
|
|
bpp = BitsPerPixel(fmt);
|
|
|
|
if ((flags & (uint)(CP_FLAGS.CP_FLAGS_LEGACY_DWORD | CP_FLAGS.CP_FLAGS_PARAGRAPH | CP_FLAGS.CP_FLAGS_YMM | CP_FLAGS.CP_FLAGS_ZMM | CP_FLAGS.CP_FLAGS_PAGE4K)) != 0)
|
|
{
|
|
if ((flags & (uint)CP_FLAGS.CP_FLAGS_PAGE4K) != 0)
|
|
{
|
|
rowPitch = ((width * bpp + 32767) / 32768) * 4096;
|
|
slicePitch = rowPitch * height;
|
|
}
|
|
else if ((flags & (uint)CP_FLAGS.CP_FLAGS_ZMM) != 0)
|
|
{
|
|
rowPitch = ((width * bpp + 511) / 512) * 64;
|
|
slicePitch = rowPitch * height;
|
|
}
|
|
else if ((flags & (uint)CP_FLAGS.CP_FLAGS_YMM) != 0)
|
|
{
|
|
rowPitch = ((width * bpp + 255) / 256) * 32;
|
|
slicePitch = rowPitch * height;
|
|
}
|
|
else if ((flags & (uint)CP_FLAGS.CP_FLAGS_PARAGRAPH) != 0)
|
|
{
|
|
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;// sizeof(uint32_t);
|
|
slicePitch = rowPitch * height;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Default byte alignment
|
|
rowPitch = (width * bpp + 7) / 8;
|
|
slicePitch = rowPitch * height;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
public static bool IsCompressed(DXGI_FORMAT fmt)
|
|
{
|
|
switch (fmt)
|
|
{
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC1_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM_SRGB:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC2_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM_SRGB:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC3_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM_SRGB:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC4_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC4_SNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC5_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC5_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC5_SNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC6H_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC6H_UF16:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC6H_SF16:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC7_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM_SRGB:
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public static bool IsPlanar(DXGI_FORMAT fmt)
|
|
{
|
|
switch (fmt)
|
|
{
|
|
case DXGI_FORMAT.DXGI_FORMAT_NV12: // 4:2:0 8-bit
|
|
case DXGI_FORMAT.DXGI_FORMAT_P010: // 4:2:0 10-bit
|
|
case DXGI_FORMAT.DXGI_FORMAT_P016: // 4:2:0 16-bit
|
|
case DXGI_FORMAT.DXGI_FORMAT_420_OPAQUE:// 4:2:0 8-bit
|
|
case DXGI_FORMAT.DXGI_FORMAT_NV11: // 4:1:1 8-bit
|
|
|
|
case DXGI_FORMAT.WIN10_DXGI_FORMAT_P208: // 4:2:2 8-bit
|
|
case DXGI_FORMAT.WIN10_DXGI_FORMAT_V208: // 4:4:0 8-bit
|
|
case DXGI_FORMAT.WIN10_DXGI_FORMAT_V408: // 4:4:4 8-bit
|
|
// These are JPEG Hardware decode formats (DXGI 1.4)
|
|
|
|
case DXGI_FORMAT.XBOX_DXGI_FORMAT_D16_UNORM_S8_UINT:
|
|
case DXGI_FORMAT.XBOX_DXGI_FORMAT_R16_UNORM_X8_TYPELESS:
|
|
case DXGI_FORMAT.XBOX_DXGI_FORMAT_X16_TYPELESS_G8_UINT:
|
|
// These are Xbox One platform specific types
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
public static bool IsPacked(DXGI_FORMAT fmt)
|
|
{
|
|
switch (fmt)
|
|
{
|
|
case DXGI_FORMAT.DXGI_FORMAT_R8G8_B8G8_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_G8R8_G8B8_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_YUY2: // 4:2:2 8-bit
|
|
case DXGI_FORMAT.DXGI_FORMAT_Y210: // 4:2:2 10-bit
|
|
case DXGI_FORMAT.DXGI_FORMAT_Y216: // 4:2:2 16-bit
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public static void assert(bool b)
|
|
{
|
|
if (!b)
|
|
{
|
|
throw new Exception();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public static bool IsValid(DXGI_FORMAT fmt)
|
|
{
|
|
return (((int)fmt) >= 1 && ((int)fmt) <= 190);
|
|
}
|
|
public static bool IsPalettized(DXGI_FORMAT fmt)
|
|
{
|
|
switch (fmt)
|
|
{
|
|
case DXGI_FORMAT.DXGI_FORMAT_AI44:
|
|
case DXGI_FORMAT.DXGI_FORMAT_IA44:
|
|
case DXGI_FORMAT.DXGI_FORMAT_P8:
|
|
case DXGI_FORMAT.DXGI_FORMAT_A8P8:
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
public static int BitsPerPixel(DXGI_FORMAT fmt)
|
|
{
|
|
switch (fmt)
|
|
{
|
|
case DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_FLOAT:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_UINT:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_SINT:
|
|
return 128;
|
|
|
|
case DXGI_FORMAT.DXGI_FORMAT_R32G32B32_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R32G32B32_FLOAT:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R32G32B32_UINT:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R32G32B32_SINT:
|
|
return 96;
|
|
|
|
case DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_FLOAT:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_UINT:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_SNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_SINT:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R32G32_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R32G32_FLOAT:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R32G32_UINT:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R32G32_SINT:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R32G8X24_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_D32_FLOAT_S8X24_UINT:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_X32_TYPELESS_G8X24_UINT:
|
|
case DXGI_FORMAT.DXGI_FORMAT_Y416:
|
|
case DXGI_FORMAT.DXGI_FORMAT_Y210:
|
|
case DXGI_FORMAT.DXGI_FORMAT_Y216:
|
|
return 64;
|
|
|
|
case DXGI_FORMAT.DXGI_FORMAT_R10G10B10A2_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R10G10B10A2_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R10G10B10A2_UINT:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R11G11B10_FLOAT:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UINT:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_SNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_SINT:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R16G16_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R16G16_FLOAT:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R16G16_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R16G16_UINT:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R16G16_SNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R16G16_SINT:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R32_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_D32_FLOAT:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R32_FLOAT:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R32_UINT:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R32_SINT:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R24G8_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_D24_UNORM_S8_UINT:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R24_UNORM_X8_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_X24_TYPELESS_G8_UINT:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R9G9B9E5_SHAREDEXP:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R8G8_B8G8_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_G8R8_G8B8_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_B8G8R8X8_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
|
|
case DXGI_FORMAT.DXGI_FORMAT_B8G8R8X8_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_B8G8R8X8_UNORM_SRGB:
|
|
case DXGI_FORMAT.DXGI_FORMAT_AYUV:
|
|
case DXGI_FORMAT.DXGI_FORMAT_Y410:
|
|
case DXGI_FORMAT.DXGI_FORMAT_YUY2:
|
|
case DXGI_FORMAT.XBOX_DXGI_FORMAT_R10G10B10_7E3_A2_FLOAT:
|
|
case DXGI_FORMAT.XBOX_DXGI_FORMAT_R10G10B10_6E4_A2_FLOAT:
|
|
case DXGI_FORMAT.XBOX_DXGI_FORMAT_R10G10B10_SNORM_A2_UNORM:
|
|
return 32;
|
|
|
|
case DXGI_FORMAT.DXGI_FORMAT_P010:
|
|
case DXGI_FORMAT.DXGI_FORMAT_P016:
|
|
case DXGI_FORMAT.XBOX_DXGI_FORMAT_D16_UNORM_S8_UINT:
|
|
case DXGI_FORMAT.XBOX_DXGI_FORMAT_R16_UNORM_X8_TYPELESS:
|
|
case DXGI_FORMAT.XBOX_DXGI_FORMAT_X16_TYPELESS_G8_UINT:
|
|
case DXGI_FORMAT.WIN10_DXGI_FORMAT_V408:
|
|
return 24;
|
|
|
|
case DXGI_FORMAT.DXGI_FORMAT_R8G8_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R8G8_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R8G8_UINT:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R8G8_SNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R8G8_SINT:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R16_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R16_FLOAT:
|
|
case DXGI_FORMAT.DXGI_FORMAT_D16_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R16_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R16_UINT:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R16_SNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R16_SINT:
|
|
case DXGI_FORMAT.DXGI_FORMAT_B5G6R5_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_B5G5R5A1_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_A8P8:
|
|
case DXGI_FORMAT.DXGI_FORMAT_B4G4R4A4_UNORM:
|
|
case DXGI_FORMAT.WIN10_DXGI_FORMAT_P208:
|
|
case DXGI_FORMAT.WIN10_DXGI_FORMAT_V208:
|
|
return 16;
|
|
|
|
case DXGI_FORMAT.DXGI_FORMAT_NV12:
|
|
case DXGI_FORMAT.DXGI_FORMAT_420_OPAQUE:
|
|
case DXGI_FORMAT.DXGI_FORMAT_NV11:
|
|
return 12;
|
|
|
|
case DXGI_FORMAT.DXGI_FORMAT_R8_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R8_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R8_UINT:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R8_SNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_R8_SINT:
|
|
case DXGI_FORMAT.DXGI_FORMAT_A8_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_AI44:
|
|
case DXGI_FORMAT.DXGI_FORMAT_IA44:
|
|
case DXGI_FORMAT.DXGI_FORMAT_P8:
|
|
case DXGI_FORMAT.XBOX_DXGI_FORMAT_R4G4_UNORM:
|
|
return 8;
|
|
|
|
case DXGI_FORMAT.DXGI_FORMAT_R1_UNORM:
|
|
return 1;
|
|
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC1_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM_SRGB:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC4_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC4_SNORM:
|
|
return 4;
|
|
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC2_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM_SRGB:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC3_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM_SRGB:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC5_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC5_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC5_SNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC6H_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC6H_UF16:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC6H_SF16:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC7_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM_SRGB:
|
|
return 8;
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public static int ComputeScanlines(DXGI_FORMAT fmt, int height)
|
|
{
|
|
switch (fmt)
|
|
{
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC1_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM_SRGB:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC2_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM_SRGB:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC3_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM_SRGB:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC4_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC4_SNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC5_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC5_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC5_SNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC6H_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC6H_UF16:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC6H_SF16:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC7_TYPELESS:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM:
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM_SRGB:
|
|
assert(IsCompressed(fmt));
|
|
return Math.Max(1, (height + 3) / 4);
|
|
|
|
case DXGI_FORMAT.DXGI_FORMAT_NV11:
|
|
case DXGI_FORMAT.WIN10_DXGI_FORMAT_P208:
|
|
assert(IsPlanar(fmt));
|
|
return height * 2;
|
|
|
|
case DXGI_FORMAT.WIN10_DXGI_FORMAT_V208:
|
|
assert(IsPlanar(fmt));
|
|
return height + (((height + 1) >> 1) * 2);
|
|
|
|
case DXGI_FORMAT.WIN10_DXGI_FORMAT_V408:
|
|
assert(IsPlanar(fmt));
|
|
return height + ((height >> 1) * 4);
|
|
|
|
case DXGI_FORMAT.DXGI_FORMAT_NV12:
|
|
case DXGI_FORMAT.DXGI_FORMAT_P010:
|
|
case DXGI_FORMAT.DXGI_FORMAT_P016:
|
|
case DXGI_FORMAT.DXGI_FORMAT_420_OPAQUE:
|
|
case DXGI_FORMAT.XBOX_DXGI_FORMAT_D16_UNORM_S8_UINT:
|
|
case DXGI_FORMAT.XBOX_DXGI_FORMAT_R16_UNORM_X8_TYPELESS:
|
|
case DXGI_FORMAT.XBOX_DXGI_FORMAT_X16_TYPELESS_G8_UINT:
|
|
assert(IsPlanar(fmt));
|
|
return height + ((height + 1) >> 1);
|
|
|
|
default:
|
|
assert(IsValid(fmt));
|
|
assert(!IsCompressed(fmt) && !IsPlanar(fmt));
|
|
return height;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static uint DDS_HEADER_FLAGS_TEXTURE = 0x00001007; // DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT
|
|
public static uint DDS_HEADER_FLAGS_MIPMAP = 0x00020000; // DDSD_MIPMAPCOUNT
|
|
public static uint DDS_HEADER_FLAGS_VOLUME = 0x00800000; // DDSD_DEPTH
|
|
public static uint DDS_HEADER_FLAGS_PITCH = 0x00000008; // DDSD_PITCH
|
|
public static uint DDS_HEADER_FLAGS_LINEARSIZE = 0x00080000; // DDSD_LINEARSIZE
|
|
|
|
public static uint DDS_HEIGHT = 0x00000002; // DDSD_HEIGHT
|
|
public static uint DDS_WIDTH = 0x00000004; // DDSD_WIDTH
|
|
|
|
public static uint DDS_SURFACE_FLAGS_TEXTURE = 0x00001000; // DDSCAPS_TEXTURE
|
|
public static uint DDS_SURFACE_FLAGS_MIPMAP = 0x00400008; // DDSCAPS_COMPLEX | DDSCAPS_MIPMAP
|
|
public static uint DDS_SURFACE_FLAGS_CUBEMAP = 0x00000008; // DDSCAPS_COMPLEX
|
|
|
|
public static uint DDS_CUBEMAP_POSITIVEX = 0x00000600; // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEX
|
|
public static uint DDS_CUBEMAP_NEGATIVEX = 0x00000a00; // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEX
|
|
public static uint DDS_CUBEMAP_POSITIVEY = 0x00001200; // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEY
|
|
public static uint DDS_CUBEMAP_NEGATIVEY = 0x00002200; // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEY
|
|
public static uint DDS_CUBEMAP_POSITIVEZ = 0x00004200; // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEZ
|
|
public static uint DDS_CUBEMAP_NEGATIVEZ = 0x00008200; // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEZ
|
|
|
|
public static uint DDS_CUBEMAP_ALLFACES = (DDS_CUBEMAP_POSITIVEX | DDS_CUBEMAP_NEGATIVEX |
|
|
DDS_CUBEMAP_POSITIVEY | DDS_CUBEMAP_NEGATIVEY |
|
|
DDS_CUBEMAP_POSITIVEZ | DDS_CUBEMAP_NEGATIVEZ);
|
|
|
|
public static uint DDS_CUBEMAP = 0x00000200; // DDSCAPS2_CUBEMAP
|
|
|
|
public static uint DDS_FLAGS_VOLUME = 0x00200000; // DDSCAPS2_VOLUME
|
|
|
|
public static uint DDS_MAGIC = 0x20534444; // "DDS "
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------------------------
|
|
// Encodes DDS file header (magic value, header, optional DX10 extended header)
|
|
//-------------------------------------------------------------------------------------
|
|
public static bool _EncodeDDSHeader(TexMetadata metadata, uint flags, BinaryWriter bw, out int required)
|
|
{
|
|
//const uint MAX_HEADER_SIZE = sizeof(uint) + 31*4/*sizeof(DDS_HEADER)*/ + 5*4/*sizeof(DDS_HEADER_DXT10)*/;
|
|
//byte header[MAX_HEADER_SIZE];
|
|
|
|
required = 0;
|
|
if (!IsValid(metadata.format))
|
|
return false;
|
|
|
|
if (IsPalettized(metadata.format))
|
|
return false;// HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
|
|
|
|
if (metadata.arraySize > 1)
|
|
{
|
|
if ((metadata.arraySize != 6) || (metadata.dimension != TEX_DIMENSION.TEX_DIMENSION_TEXTURE2D) || !(metadata.IsCubemap()))
|
|
{
|
|
// Texture1D arrays, Texture2D arrays, and Cubemap arrays must be stored using 'DX10' extended header
|
|
flags |= (uint)DDS_FLAGS.DDS_FLAGS_FORCE_DX10_EXT;
|
|
}
|
|
}
|
|
|
|
if ((flags & (uint)DDS_FLAGS.DDS_FLAGS_FORCE_DX10_EXT_MISC2)!=0)
|
|
{
|
|
flags |= (uint)DDS_FLAGS.DDS_FLAGS_FORCE_DX10_EXT;
|
|
}
|
|
|
|
DDS_PIXELFORMAT ddpf = new DDS_PIXELFORMAT(0);
|
|
if (!((flags & (uint)DDS_FLAGS.DDS_FLAGS_FORCE_DX10_EXT)!=0))
|
|
{
|
|
switch (metadata.format)
|
|
{
|
|
case DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM: ddpf = DDS_PIXELFORMAT.DDSPF_A8B8G8R8; break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_R16G16_UNORM: ddpf = DDS_PIXELFORMAT.DDSPF_G16R16; break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_R8G8_UNORM: ddpf = DDS_PIXELFORMAT.DDSPF_A8L8; break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_R16_UNORM: ddpf = DDS_PIXELFORMAT.DDSPF_L16; break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_R8_UNORM: ddpf = DDS_PIXELFORMAT.DDSPF_L8; break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_A8_UNORM: ddpf = DDS_PIXELFORMAT.DDSPF_A8; break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_R8G8_B8G8_UNORM: ddpf = DDS_PIXELFORMAT.DDSPF_R8G8_B8G8; break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_G8R8_G8B8_UNORM: ddpf = DDS_PIXELFORMAT.DDSPF_G8R8_G8B8; break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM: ddpf = DDS_PIXELFORMAT.DDSPF_DXT1; break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM: ddpf = metadata.IsPMAlpha() ? (DDS_PIXELFORMAT.DDSPF_DXT2) : (DDS_PIXELFORMAT.DDSPF_DXT3); break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM: ddpf = metadata.IsPMAlpha() ? (DDS_PIXELFORMAT.DDSPF_DXT4) : (DDS_PIXELFORMAT.DDSPF_DXT5); break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM: ddpf = DDS_PIXELFORMAT.DDSPF_BC4_UNORM; break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC4_SNORM: ddpf = DDS_PIXELFORMAT.DDSPF_BC4_SNORM; break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC5_UNORM: ddpf = DDS_PIXELFORMAT.DDSPF_BC5_UNORM; break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_BC5_SNORM: ddpf = DDS_PIXELFORMAT.DDSPF_BC5_SNORM; break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_B5G6R5_UNORM: ddpf = DDS_PIXELFORMAT.DDSPF_R5G6B5; break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_B5G5R5A1_UNORM: ddpf = DDS_PIXELFORMAT.DDSPF_A1R5G5B5; break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM: ddpf = DDS_PIXELFORMAT.DDSPF_A8R8G8B8; break; // DXGI 1.1
|
|
case DXGI_FORMAT.DXGI_FORMAT_B8G8R8X8_UNORM: ddpf = DDS_PIXELFORMAT.DDSPF_X8R8G8B8; break; // DXGI 1.1
|
|
case DXGI_FORMAT.DXGI_FORMAT_B4G4R4A4_UNORM: ddpf = DDS_PIXELFORMAT.DDSPF_A4R4G4B4; break; // DXGI 1.2
|
|
case DXGI_FORMAT.DXGI_FORMAT_YUY2: ddpf = DDS_PIXELFORMAT.DDSPF_YUY2; break; // DXGI 1.2
|
|
|
|
// Legacy D3DX formats using D3DFMT enum value as FourCC
|
|
case DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_FLOAT:
|
|
ddpf.dwSize = 32/*sizeof(DDS_PIXELFORMAT)*/; ddpf.dwFlags = DDS_PIXELFORMAT.DDS_FOURCC; ddpf.dwFourCC = 116; // D3DFMT_A32B32G32R32F
|
|
break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_FLOAT:
|
|
ddpf.dwSize = 32/*sizeof(DDS_PIXELFORMAT)*/; ddpf.dwFlags = DDS_PIXELFORMAT.DDS_FOURCC; ddpf.dwFourCC = 113; // D3DFMT_A16B16G16R16F
|
|
break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_UNORM:
|
|
ddpf.dwSize = 32/*sizeof(DDS_PIXELFORMAT)*/; ddpf.dwFlags = DDS_PIXELFORMAT.DDS_FOURCC; ddpf.dwFourCC = 36; // D3DFMT_A16B16G16R16
|
|
break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_SNORM:
|
|
ddpf.dwSize = 32/*sizeof(DDS_PIXELFORMAT)*/; ddpf.dwFlags = DDS_PIXELFORMAT.DDS_FOURCC; ddpf.dwFourCC = 110; // D3DFMT_Q16W16V16U16
|
|
break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_R32G32_FLOAT:
|
|
ddpf.dwSize = 32/*sizeof(DDS_PIXELFORMAT)*/; ddpf.dwFlags = DDS_PIXELFORMAT.DDS_FOURCC; ddpf.dwFourCC = 115; // D3DFMT_G32R32F
|
|
break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_R16G16_FLOAT:
|
|
ddpf.dwSize = 32/*sizeof(DDS_PIXELFORMAT)*/; ddpf.dwFlags = DDS_PIXELFORMAT.DDS_FOURCC; ddpf.dwFourCC = 112; // D3DFMT_G16R16F
|
|
break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_R32_FLOAT:
|
|
ddpf.dwSize = 32/*sizeof(DDS_PIXELFORMAT)*/; ddpf.dwFlags = DDS_PIXELFORMAT.DDS_FOURCC; ddpf.dwFourCC = 114; // D3DFMT_R32F
|
|
break;
|
|
case DXGI_FORMAT.DXGI_FORMAT_R16_FLOAT:
|
|
ddpf.dwSize = 32/*sizeof(DDS_PIXELFORMAT)*/; ddpf.dwFlags = DDS_PIXELFORMAT.DDS_FOURCC; ddpf.dwFourCC = 111; // D3DFMT_R16F
|
|
break;
|
|
}
|
|
}
|
|
|
|
int sizeofddsheader = 31 * 4;
|
|
int sizeofddsheader10 = 5 * 4;
|
|
|
|
required = 4/*sizeof(uint32_t)*/ + sizeofddsheader/*sizeof(DDS_HEADER)*/;
|
|
|
|
if (ddpf.dwSize == 0)
|
|
required += sizeofddsheader10;// sizeof(DDS_HEADER_DXT10);
|
|
|
|
//if (!pDestination)
|
|
// return S_OK;
|
|
|
|
//if (maxsize < required)
|
|
// return E_NOT_SUFFICIENT_BUFFER;
|
|
|
|
//*reinterpret_cast<uint32_t*>(pDestination) = DDS_MAGIC;
|
|
//bw.Write(DDS_MAGIC);
|
|
|
|
var header = new DDS_HEADER();
|
|
//auto header = reinterpret_cast<DDS_HEADER*>(reinterpret_cast<uint8_t*>(pDestination) + sizeof(uint32_t));
|
|
//assert(header);
|
|
|
|
//memset(header, 0, sizeof(DDS_HEADER));
|
|
header.dwSize = (uint)sizeofddsheader;// sizeof(DDS_HEADER);
|
|
header.dwFlags = DDS_HEADER_FLAGS_TEXTURE;
|
|
header.dwCaps = DDS_SURFACE_FLAGS_TEXTURE;
|
|
|
|
if (metadata.mipLevels > 0)
|
|
{
|
|
header.dwFlags |= DDS_HEADER_FLAGS_MIPMAP;
|
|
|
|
//# ifdef _M_X64
|
|
// if (metadata.mipLevels > 0xFFFFFFFF)
|
|
// return E_INVALIDARG;
|
|
//#endif
|
|
|
|
header.dwMipMapCount = (uint)(metadata.mipLevels);
|
|
|
|
if (header.dwMipMapCount > 1)
|
|
header.dwCaps |= DDS_SURFACE_FLAGS_MIPMAP;
|
|
}
|
|
|
|
switch (metadata.dimension)
|
|
{
|
|
case TEX_DIMENSION.TEX_DIMENSION_TEXTURE1D:
|
|
//# ifdef _M_X64
|
|
// if (metadata.width > 0xFFFFFFFF)
|
|
// return E_INVALIDARG;
|
|
//#endif
|
|
|
|
header.dwWidth = (uint)(metadata.width);
|
|
header.dwHeight = header.dwDepth = 1;
|
|
break;
|
|
|
|
case TEX_DIMENSION.TEX_DIMENSION_TEXTURE2D:
|
|
//# ifdef _M_X64
|
|
// if (metadata.height > 0xFFFFFFFF
|
|
// || metadata.width > 0xFFFFFFFF)
|
|
// return E_INVALIDARG;
|
|
//#endif
|
|
|
|
header.dwHeight = (uint)(metadata.height);
|
|
header.dwWidth = (uint)(metadata.width);
|
|
header.dwDepth = 1;
|
|
|
|
if (metadata.IsCubemap())
|
|
{
|
|
header.dwCaps |= DDS_SURFACE_FLAGS_CUBEMAP;
|
|
header.dwCaps2 |= DDS_CUBEMAP_ALLFACES;
|
|
}
|
|
break;
|
|
|
|
case TEX_DIMENSION.TEX_DIMENSION_TEXTURE3D:
|
|
//# ifdef _M_X64
|
|
// if (metadata.height > 0xFFFFFFFF
|
|
// || metadata.width > 0xFFFFFFFF
|
|
// || metadata.depth > 0xFFFFFFFF)
|
|
// return E_INVALIDARG;
|
|
//#endif
|
|
|
|
header.dwFlags |= DDS_HEADER_FLAGS_VOLUME;
|
|
header.dwCaps2 |= DDS_FLAGS_VOLUME;
|
|
header.dwHeight = (uint)(metadata.height);
|
|
header.dwWidth = (uint)(metadata.width);
|
|
header.dwDepth = (uint)(metadata.depth);
|
|
break;
|
|
|
|
default:
|
|
return false;// E_FAIL;
|
|
}
|
|
|
|
int rowPitch, slicePitch;
|
|
ComputePitch(metadata.format, metadata.width, metadata.height, out rowPitch, out slicePitch, 0);// (uint)CP_FLAGS.CP_FLAGS_NONE);
|
|
|
|
//# ifdef _M_X64
|
|
// if (slicePitch > 0xFFFFFFFF
|
|
// || rowPitch > 0xFFFFFFFF)
|
|
// return E_FAIL;
|
|
//#endif
|
|
|
|
if (IsCompressed(metadata.format))
|
|
{
|
|
header.dwFlags |= DDS_HEADER_FLAGS_LINEARSIZE;
|
|
header.dwPitchOrLinearSize = (uint)(slicePitch);
|
|
}
|
|
else
|
|
{
|
|
header.dwFlags |= DDS_HEADER_FLAGS_PITCH;
|
|
header.dwPitchOrLinearSize = (uint)(rowPitch);
|
|
}
|
|
|
|
var ext = new DDS_HEADER_DXT10();
|
|
if (ddpf.dwSize == 0)
|
|
{
|
|
header.ddspf = DDS_PIXELFORMAT.DDSPF_DX10;
|
|
//memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_DX10, sizeof(DDS_PIXELFORMAT) );
|
|
|
|
//auto ext = reinterpret_cast<DDS_HEADER_DXT10*>(reinterpret_cast<uint8_t*>(header) + sizeof(DDS_HEADER));
|
|
//assert(ext);
|
|
//var ext = new DDS_HEADER_DXT10();
|
|
|
|
//memset(ext, 0, sizeof(DDS_HEADER_DXT10));
|
|
ext.dxgiFormat = metadata.format;
|
|
ext.resourceDimension = (uint)metadata.dimension;
|
|
|
|
//# ifdef _M_X64
|
|
// if (metadata.arraySize > 0xFFFFFFFF)
|
|
// return E_INVALIDARG;
|
|
//#endif
|
|
|
|
//static_assert(TEX_MISC_TEXTURECUBE == DDS_RESOURCE_MISC_TEXTURECUBE, "DDS header mismatch");
|
|
|
|
ext.miscFlag = metadata.miscFlags & ~((uint)TEX_MISC_FLAG.TEX_MISC_TEXTURECUBE);
|
|
|
|
if ((metadata.miscFlags & (uint)TEX_MISC_FLAG.TEX_MISC_TEXTURECUBE)!=0)
|
|
{
|
|
ext.miscFlag |= (uint)TEX_MISC_FLAG.TEX_MISC_TEXTURECUBE;
|
|
assert((metadata.arraySize % 6) == 0);
|
|
ext.arraySize = (uint)(metadata.arraySize / 6);
|
|
}
|
|
else
|
|
{
|
|
ext.arraySize = (uint)(metadata.arraySize);
|
|
}
|
|
|
|
//static_assert(TEX_MISC2_ALPHA_MODE_MASK == DDS_MISC_FLAGS2_ALPHA_MODE_MASK, "DDS header mismatch");
|
|
//static_assert(TEX_ALPHA_MODE_UNKNOWN == DDS_ALPHA_MODE_UNKNOWN, "DDS header mismatch");
|
|
//static_assert(TEX_ALPHA_MODE_STRAIGHT == DDS_ALPHA_MODE_STRAIGHT, "DDS header mismatch");
|
|
//static_assert(TEX_ALPHA_MODE_PREMULTIPLIED == DDS_ALPHA_MODE_PREMULTIPLIED, "DDS header mismatch");
|
|
//static_assert(TEX_ALPHA_MODE_OPAQUE == DDS_ALPHA_MODE_OPAQUE, "DDS header mismatch");
|
|
//static_assert(TEX_ALPHA_MODE_CUSTOM == DDS_ALPHA_MODE_CUSTOM, "DDS header mismatch");
|
|
|
|
if ((flags & (uint)DDS_FLAGS.DDS_FLAGS_FORCE_DX10_EXT_MISC2)!=0)
|
|
{
|
|
// This was formerly 'reserved'. D3DX10 and D3DX11 will fail if this value is anything other than 0
|
|
ext.miscFlags2 = metadata.miscFlags2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
header.ddspf = ddpf;
|
|
//memcpy_s(&header.ddspf, sizeof(header.ddspf), &ddpf, sizeof(ddpf) );
|
|
}
|
|
|
|
|
|
//magic write
|
|
bw.Write(DDS_MAGIC);
|
|
|
|
//write header
|
|
bw.Write(header.dwSize);
|
|
bw.Write(header.dwFlags);
|
|
bw.Write(header.dwHeight);
|
|
bw.Write(header.dwWidth);
|
|
bw.Write(header.dwPitchOrLinearSize);
|
|
bw.Write(header.dwDepth); // only if DDS_HEADER_FLAGS_VOLUME is set in dwFlags
|
|
bw.Write(header.dwMipMapCount);
|
|
|
|
//public uint dwReserved1[11]; //x11
|
|
for (int i = 0; i < 11; i++) bw.Write(0);
|
|
|
|
//bw.Write(header.ddspf);
|
|
bw.Write(header.ddspf.dwSize);
|
|
bw.Write(header.ddspf.dwFlags);
|
|
bw.Write(header.ddspf.dwFourCC);
|
|
bw.Write(header.ddspf.dwRGBBitCount);
|
|
bw.Write(header.ddspf.dwRBitMask);
|
|
bw.Write(header.ddspf.dwGBitMask);
|
|
bw.Write(header.ddspf.dwBBitMask);
|
|
bw.Write(header.ddspf.dwABitMask);
|
|
|
|
bw.Write(header.dwCaps);
|
|
bw.Write(header.dwCaps2);
|
|
bw.Write(header.dwCaps3);
|
|
bw.Write(header.dwCaps4);
|
|
bw.Write(header.dwReserved2);
|
|
|
|
|
|
if (ddpf.dwSize == 0)
|
|
{
|
|
//write ext
|
|
bw.Write((uint)ext.dxgiFormat);
|
|
bw.Write(ext.resourceDimension);
|
|
bw.Write(ext.miscFlag); // see DDS_RESOURCE_MISC_FLAG
|
|
bw.Write(ext.arraySize);
|
|
bw.Write(ext.miscFlags2); // see DDS_MISC_FLAGS2
|
|
}
|
|
|
|
return true; //S_OK
|
|
}
|
|
|
|
|
|
public static bool _ReadDDSHeader(BinaryReader br, out DDS_HEADER header, out DDS_HEADER_DXT10 header10, out bool useheader10)
|
|
{
|
|
var magic = br.ReadUInt32();
|
|
if (magic != DDS_MAGIC) throw new Exception("Invalid DDS magic!");
|
|
|
|
//var header = new DDS_HEADER();
|
|
//var header10 = new DDS_HEADER_DXT10();
|
|
int sizeofddsheader = 31 * 4;
|
|
int sizeofddspixelformat = 8 * 4;
|
|
|
|
header.dwSize = br.ReadUInt32();
|
|
header.dwFlags = br.ReadUInt32();
|
|
header.dwHeight = br.ReadUInt32();
|
|
header.dwWidth = br.ReadUInt32();
|
|
header.dwPitchOrLinearSize = br.ReadUInt32();
|
|
header.dwDepth = br.ReadUInt32(); // only if DDS_HEADER_FLAGS_VOLUME is set in dwFlags
|
|
header.dwMipMapCount = br.ReadUInt32();
|
|
|
|
//public uint dwReserved1[11]; //x11
|
|
for (int i = 0; i < 11; i++) br.ReadUInt32();
|
|
|
|
header.ddspf.dwSize = br.ReadUInt32();
|
|
header.ddspf.dwFlags = br.ReadUInt32();
|
|
header.ddspf.dwFourCC = br.ReadUInt32();
|
|
header.ddspf.dwRGBBitCount = br.ReadUInt32();
|
|
header.ddspf.dwRBitMask = br.ReadUInt32();
|
|
header.ddspf.dwGBitMask = br.ReadUInt32();
|
|
header.ddspf.dwBBitMask = br.ReadUInt32();
|
|
header.ddspf.dwABitMask = br.ReadUInt32();
|
|
|
|
header.dwCaps = br.ReadUInt32();
|
|
header.dwCaps2 = br.ReadUInt32();
|
|
header.dwCaps3 = br.ReadUInt32();
|
|
header.dwCaps4 = br.ReadUInt32();
|
|
header.dwReserved2 = br.ReadUInt32();
|
|
|
|
|
|
|
|
if(((DDS_PIXELFORMAT.DDS_FOURCC & header.ddspf.dwFlags) > 0) &&
|
|
(DDS_PIXELFORMAT.MAKEFOURCC('D', 'X', '1', '0') == header.ddspf.dwFourCC))
|
|
{
|
|
header10.dxgiFormat = (DXGI_FORMAT)br.ReadUInt32();
|
|
header10.resourceDimension = br.ReadUInt32();
|
|
header10.miscFlag = br.ReadUInt32(); // see DDS_RESOURCE_MISC_FLAG
|
|
header10.arraySize = br.ReadUInt32();
|
|
header10.miscFlags2 = br.ReadUInt32(); // see DDS_MISC_FLAGS2
|
|
useheader10 = true;
|
|
}
|
|
else
|
|
{
|
|
header10 = new DDS_HEADER_DXT10();
|
|
useheader10 = false;
|
|
}
|
|
|
|
if (header.dwSize != sizeofddsheader || header.ddspf.dwSize != sizeofddspixelformat)
|
|
{
|
|
throw new Exception("Invalid DDS header size");
|
|
}
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
public enum CP_FLAGS : uint
|
|
{
|
|
CP_FLAGS_NONE = 0x0, // Normal operation
|
|
CP_FLAGS_LEGACY_DWORD = 0x1, // Assume pitch is DWORD aligned instead of BYTE aligned
|
|
CP_FLAGS_PARAGRAPH = 0x2, // Assume pitch is 16-byte aligned instead of BYTE aligned
|
|
CP_FLAGS_YMM = 0x4, // Assume pitch is 32-byte aligned instead of BYTE aligned
|
|
CP_FLAGS_ZMM = 0x8, // Assume pitch is 64-byte aligned instead of BYTE aligned
|
|
CP_FLAGS_PAGE4K = 0x200, // Assume pitch is 4096-byte aligned instead of BYTE aligned
|
|
CP_FLAGS_24BPP = 0x10000, // Override with a legacy 24 bits-per-pixel format size
|
|
CP_FLAGS_16BPP = 0x20000, // Override with a legacy 16 bits-per-pixel format size
|
|
CP_FLAGS_8BPP = 0x40000, // Override with a legacy 8 bits-per-pixel format size
|
|
}
|
|
|
|
|
|
public class ImageStruct
|
|
{
|
|
public int Width;
|
|
public int Height;
|
|
//property int Stride;
|
|
public int Format;
|
|
public int MipMapLevels;
|
|
public byte[] Data;
|
|
|
|
int GetRowPitch()
|
|
{
|
|
int rowPitch;
|
|
int slicePitch;
|
|
DXTex.ComputePitch((DXGI_FORMAT)Format, Width, Height, out rowPitch, out slicePitch, 0);
|
|
return rowPitch;
|
|
}
|
|
|
|
int GetSlicePitch()
|
|
{
|
|
int rowPitch;
|
|
int slicePitch;
|
|
DXTex.ComputePitch((DXGI_FORMAT)Format, Width, Height, out rowPitch, out slicePitch, 0);
|
|
return slicePitch;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
public enum DXGI_FORMAT : uint
|
|
{
|
|
DXGI_FORMAT_UNKNOWN = 0,
|
|
DXGI_FORMAT_R32G32B32A32_TYPELESS = 1,
|
|
DXGI_FORMAT_R32G32B32A32_FLOAT = 2,
|
|
DXGI_FORMAT_R32G32B32A32_UINT = 3,
|
|
DXGI_FORMAT_R32G32B32A32_SINT = 4,
|
|
DXGI_FORMAT_R32G32B32_TYPELESS = 5,
|
|
DXGI_FORMAT_R32G32B32_FLOAT = 6,
|
|
DXGI_FORMAT_R32G32B32_UINT = 7,
|
|
DXGI_FORMAT_R32G32B32_SINT = 8,
|
|
DXGI_FORMAT_R16G16B16A16_TYPELESS = 9,
|
|
DXGI_FORMAT_R16G16B16A16_FLOAT = 10,
|
|
DXGI_FORMAT_R16G16B16A16_UNORM = 11,
|
|
DXGI_FORMAT_R16G16B16A16_UINT = 12,
|
|
DXGI_FORMAT_R16G16B16A16_SNORM = 13,
|
|
DXGI_FORMAT_R16G16B16A16_SINT = 14,
|
|
DXGI_FORMAT_R32G32_TYPELESS = 15,
|
|
DXGI_FORMAT_R32G32_FLOAT = 16,
|
|
DXGI_FORMAT_R32G32_UINT = 17,
|
|
DXGI_FORMAT_R32G32_SINT = 18,
|
|
DXGI_FORMAT_R32G8X24_TYPELESS = 19,
|
|
DXGI_FORMAT_D32_FLOAT_S8X24_UINT = 20,
|
|
DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS = 21,
|
|
DXGI_FORMAT_X32_TYPELESS_G8X24_UINT = 22,
|
|
DXGI_FORMAT_R10G10B10A2_TYPELESS = 23,
|
|
DXGI_FORMAT_R10G10B10A2_UNORM = 24,
|
|
DXGI_FORMAT_R10G10B10A2_UINT = 25,
|
|
DXGI_FORMAT_R11G11B10_FLOAT = 26,
|
|
DXGI_FORMAT_R8G8B8A8_TYPELESS = 27,
|
|
DXGI_FORMAT_R8G8B8A8_UNORM = 28,
|
|
DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = 29,
|
|
DXGI_FORMAT_R8G8B8A8_UINT = 30,
|
|
DXGI_FORMAT_R8G8B8A8_SNORM = 31,
|
|
DXGI_FORMAT_R8G8B8A8_SINT = 32,
|
|
DXGI_FORMAT_R16G16_TYPELESS = 33,
|
|
DXGI_FORMAT_R16G16_FLOAT = 34,
|
|
DXGI_FORMAT_R16G16_UNORM = 35,
|
|
DXGI_FORMAT_R16G16_UINT = 36,
|
|
DXGI_FORMAT_R16G16_SNORM = 37,
|
|
DXGI_FORMAT_R16G16_SINT = 38,
|
|
DXGI_FORMAT_R32_TYPELESS = 39,
|
|
DXGI_FORMAT_D32_FLOAT = 40,
|
|
DXGI_FORMAT_R32_FLOAT = 41,
|
|
DXGI_FORMAT_R32_UINT = 42,
|
|
DXGI_FORMAT_R32_SINT = 43,
|
|
DXGI_FORMAT_R24G8_TYPELESS = 44,
|
|
DXGI_FORMAT_D24_UNORM_S8_UINT = 45,
|
|
DXGI_FORMAT_R24_UNORM_X8_TYPELESS = 46,
|
|
DXGI_FORMAT_X24_TYPELESS_G8_UINT = 47,
|
|
DXGI_FORMAT_R8G8_TYPELESS = 48,
|
|
DXGI_FORMAT_R8G8_UNORM = 49,
|
|
DXGI_FORMAT_R8G8_UINT = 50,
|
|
DXGI_FORMAT_R8G8_SNORM = 51,
|
|
DXGI_FORMAT_R8G8_SINT = 52,
|
|
DXGI_FORMAT_R16_TYPELESS = 53,
|
|
DXGI_FORMAT_R16_FLOAT = 54,
|
|
DXGI_FORMAT_D16_UNORM = 55,
|
|
DXGI_FORMAT_R16_UNORM = 56,
|
|
DXGI_FORMAT_R16_UINT = 57,
|
|
DXGI_FORMAT_R16_SNORM = 58,
|
|
DXGI_FORMAT_R16_SINT = 59,
|
|
DXGI_FORMAT_R8_TYPELESS = 60,
|
|
DXGI_FORMAT_R8_UNORM = 61,
|
|
DXGI_FORMAT_R8_UINT = 62,
|
|
DXGI_FORMAT_R8_SNORM = 63,
|
|
DXGI_FORMAT_R8_SINT = 64,
|
|
DXGI_FORMAT_A8_UNORM = 65,
|
|
DXGI_FORMAT_R1_UNORM = 66,
|
|
DXGI_FORMAT_R9G9B9E5_SHAREDEXP = 67,
|
|
DXGI_FORMAT_R8G8_B8G8_UNORM = 68,
|
|
DXGI_FORMAT_G8R8_G8B8_UNORM = 69,
|
|
DXGI_FORMAT_BC1_TYPELESS = 70,
|
|
DXGI_FORMAT_BC1_UNORM = 71,
|
|
DXGI_FORMAT_BC1_UNORM_SRGB = 72,
|
|
DXGI_FORMAT_BC2_TYPELESS = 73,
|
|
DXGI_FORMAT_BC2_UNORM = 74,
|
|
DXGI_FORMAT_BC2_UNORM_SRGB = 75,
|
|
DXGI_FORMAT_BC3_TYPELESS = 76,
|
|
DXGI_FORMAT_BC3_UNORM = 77,
|
|
DXGI_FORMAT_BC3_UNORM_SRGB = 78,
|
|
DXGI_FORMAT_BC4_TYPELESS = 79,
|
|
DXGI_FORMAT_BC4_UNORM = 80,
|
|
DXGI_FORMAT_BC4_SNORM = 81,
|
|
DXGI_FORMAT_BC5_TYPELESS = 82,
|
|
DXGI_FORMAT_BC5_UNORM = 83,
|
|
DXGI_FORMAT_BC5_SNORM = 84,
|
|
DXGI_FORMAT_B5G6R5_UNORM = 85,
|
|
DXGI_FORMAT_B5G5R5A1_UNORM = 86,
|
|
DXGI_FORMAT_B8G8R8A8_UNORM = 87,
|
|
DXGI_FORMAT_B8G8R8X8_UNORM = 88,
|
|
DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM = 89,
|
|
DXGI_FORMAT_B8G8R8A8_TYPELESS = 90,
|
|
DXGI_FORMAT_B8G8R8A8_UNORM_SRGB = 91,
|
|
DXGI_FORMAT_B8G8R8X8_TYPELESS = 92,
|
|
DXGI_FORMAT_B8G8R8X8_UNORM_SRGB = 93,
|
|
DXGI_FORMAT_BC6H_TYPELESS = 94,
|
|
DXGI_FORMAT_BC6H_UF16 = 95,
|
|
DXGI_FORMAT_BC6H_SF16 = 96,
|
|
DXGI_FORMAT_BC7_TYPELESS = 97,
|
|
DXGI_FORMAT_BC7_UNORM = 98,
|
|
DXGI_FORMAT_BC7_UNORM_SRGB = 99,
|
|
DXGI_FORMAT_AYUV = 100,
|
|
DXGI_FORMAT_Y410 = 101,
|
|
DXGI_FORMAT_Y416 = 102,
|
|
DXGI_FORMAT_NV12 = 103,
|
|
DXGI_FORMAT_P010 = 104,
|
|
DXGI_FORMAT_P016 = 105,
|
|
DXGI_FORMAT_420_OPAQUE = 106,
|
|
DXGI_FORMAT_YUY2 = 107,
|
|
DXGI_FORMAT_Y210 = 108,
|
|
DXGI_FORMAT_Y216 = 109,
|
|
DXGI_FORMAT_NV11 = 110,
|
|
DXGI_FORMAT_AI44 = 111,
|
|
DXGI_FORMAT_IA44 = 112,
|
|
DXGI_FORMAT_P8 = 113,
|
|
DXGI_FORMAT_A8P8 = 114,
|
|
DXGI_FORMAT_B4G4R4A4_UNORM = 115,
|
|
DXGI_FORMAT_FORCE_UINT = 0xffffffff,
|
|
|
|
|
|
XBOX_DXGI_FORMAT_R10G10B10_7E3_A2_FLOAT = 116,
|
|
XBOX_DXGI_FORMAT_R10G10B10_6E4_A2_FLOAT = 117,
|
|
XBOX_DXGI_FORMAT_D16_UNORM_S8_UINT = 118,
|
|
XBOX_DXGI_FORMAT_R16_UNORM_X8_TYPELESS = 119,
|
|
XBOX_DXGI_FORMAT_X16_TYPELESS_G8_UINT = 120,
|
|
|
|
WIN10_DXGI_FORMAT_P208 = 130,
|
|
WIN10_DXGI_FORMAT_V208 = 131,
|
|
WIN10_DXGI_FORMAT_V408 = 132,
|
|
|
|
XBOX_DXGI_FORMAT_R10G10B10_SNORM_A2_UNORM = 189,
|
|
XBOX_DXGI_FORMAT_R4G4_UNORM = 190,
|
|
|
|
|
|
}
|
|
|
|
public enum TEX_DIMENSION : uint
|
|
{ // Subset here matches D3D10_RESOURCE_DIMENSION and D3D11_RESOURCE_DIMENSION
|
|
TEX_DIMENSION_TEXTURE1D = 2,
|
|
TEX_DIMENSION_TEXTURE2D = 3,
|
|
TEX_DIMENSION_TEXTURE3D = 4,
|
|
}
|
|
|
|
public enum TEX_MISC_FLAG : long // Subset here matches D3D10_RESOURCE_MISC_FLAG and D3D11_RESOURCE_MISC_FLAG
|
|
{
|
|
TEX_MISC_TEXTURECUBE = 0x4L,
|
|
TEX_MISC2_ALPHA_MODE_MASK = 0x7L,
|
|
}
|
|
|
|
public enum TEX_ALPHA_MODE // Matches DDS_ALPHA_MODE, encoded in MISC_FLAGS2
|
|
{
|
|
TEX_ALPHA_MODE_UNKNOWN = 0,
|
|
TEX_ALPHA_MODE_STRAIGHT = 1,
|
|
TEX_ALPHA_MODE_PREMULTIPLIED = 2,
|
|
TEX_ALPHA_MODE_OPAQUE = 3,
|
|
TEX_ALPHA_MODE_CUSTOM = 4,
|
|
}
|
|
|
|
|
|
public struct DDS_HEADER
|
|
{
|
|
public uint dwSize;
|
|
public uint dwFlags;
|
|
public uint dwHeight;
|
|
public uint dwWidth;
|
|
public uint dwPitchOrLinearSize;
|
|
public uint dwDepth; // only if DDS_HEADER_FLAGS_VOLUME is set in dwFlags
|
|
public uint dwMipMapCount;
|
|
//public uint dwReserved1[11]; //x11
|
|
public DDS_PIXELFORMAT ddspf;
|
|
public uint dwCaps;
|
|
public uint dwCaps2;
|
|
public uint dwCaps3;
|
|
public uint dwCaps4;
|
|
public uint dwReserved2;
|
|
};
|
|
|
|
public struct DDS_HEADER_DXT10
|
|
{
|
|
public DXGI_FORMAT dxgiFormat;
|
|
public uint resourceDimension;
|
|
public uint miscFlag; // see DDS_RESOURCE_MISC_FLAG
|
|
public uint arraySize;
|
|
public uint miscFlags2; // see DDS_MISC_FLAGS2
|
|
};
|
|
|
|
public struct DDS_PIXELFORMAT
|
|
{
|
|
public uint dwSize;
|
|
public uint dwFlags;
|
|
public uint dwFourCC;
|
|
public uint dwRGBBitCount;
|
|
public uint dwRBitMask;
|
|
public uint dwGBitMask;
|
|
public uint dwBBitMask;
|
|
public uint dwABitMask;
|
|
|
|
public DDS_PIXELFORMAT(uint val)
|
|
{
|
|
dwSize = val;
|
|
dwFlags = val;
|
|
dwFourCC = val;
|
|
dwRGBBitCount = val;
|
|
dwRBitMask = val;
|
|
dwGBitMask = val;
|
|
dwBBitMask = val;
|
|
dwABitMask = val;
|
|
}
|
|
public DDS_PIXELFORMAT(uint size, uint flags, uint fourcc, uint rgbbitcount, uint rmask, uint gmask, uint bmask, uint amask)
|
|
{
|
|
dwSize = size;
|
|
dwFlags = flags;
|
|
dwFourCC = fourcc;
|
|
dwRGBBitCount = rgbbitcount;
|
|
dwRBitMask = rmask;
|
|
dwGBitMask = gmask;
|
|
dwBBitMask = bmask;
|
|
dwABitMask = amask;
|
|
}
|
|
|
|
|
|
public static uint MAKEFOURCC(char ch0, char ch1, char ch2, char ch3)
|
|
{
|
|
return
|
|
((uint)(byte)(ch0) | ((uint)(byte)(ch1) << 8) |
|
|
((uint)(byte)(ch2) << 16) | ((uint)(byte)(ch3) << 24));
|
|
}
|
|
public static uint DDS_FOURCC = 0x00000004; // DDPF_FOURCC
|
|
public static uint DDS_RGB = 0x00000040; // DDPF_RGB
|
|
public static uint DDS_RGBA = 0x00000041; // DDPF_RGB | DDPF_ALPHAPIXELS
|
|
public static uint DDS_LUMINANCE = 0x00020000; // DDPF_LUMINANCE
|
|
public static uint DDS_LUMINANCEA = 0x00020001; // DDPF_LUMINANCE | DDPF_ALPHAPIXELS
|
|
public static uint DDS_ALPHA = 0x00000002; // DDPF_ALPHA
|
|
public static uint DDS_PAL8 = 0x00000020; // DDPF_PALETTEINDEXED8
|
|
|
|
|
|
public static DDS_PIXELFORMAT DDSPF_DXT1 = new DDS_PIXELFORMAT(32, DDS_FOURCC, MAKEFOURCC('D','X','T','1'), 0, 0, 0, 0, 0 );
|
|
public static DDS_PIXELFORMAT DDSPF_DXT2 = new DDS_PIXELFORMAT(32, DDS_FOURCC, MAKEFOURCC('D', 'X', 'T', '2'), 0, 0, 0, 0, 0);
|
|
public static DDS_PIXELFORMAT DDSPF_DXT3 = new DDS_PIXELFORMAT(32, DDS_FOURCC, MAKEFOURCC('D','X','T','3'), 0, 0, 0, 0, 0);
|
|
public static DDS_PIXELFORMAT DDSPF_DXT4 = new DDS_PIXELFORMAT(32, DDS_FOURCC, MAKEFOURCC('D','X','T','4'), 0, 0, 0, 0, 0);
|
|
public static DDS_PIXELFORMAT DDSPF_DXT5 = new DDS_PIXELFORMAT(32, DDS_FOURCC, MAKEFOURCC('D','X','T','5'), 0, 0, 0, 0, 0);
|
|
public static DDS_PIXELFORMAT DDSPF_BC4_UNORM = new DDS_PIXELFORMAT(32, DDS_FOURCC, MAKEFOURCC('B','C','4','U'), 0, 0, 0, 0, 0);
|
|
public static DDS_PIXELFORMAT DDSPF_BC4_SNORM = new DDS_PIXELFORMAT(32, DDS_FOURCC, MAKEFOURCC('B','C','4','S'), 0, 0, 0, 0, 0);
|
|
public static DDS_PIXELFORMAT DDSPF_BC5_UNORM = new DDS_PIXELFORMAT(32, DDS_FOURCC, MAKEFOURCC('B','C','5','U'), 0, 0, 0, 0, 0);
|
|
public static DDS_PIXELFORMAT DDSPF_BC5_SNORM = new DDS_PIXELFORMAT(32, DDS_FOURCC, MAKEFOURCC('B','C','5','S'), 0, 0, 0, 0, 0);
|
|
public static DDS_PIXELFORMAT DDSPF_R8G8_B8G8 = new DDS_PIXELFORMAT(32, DDS_FOURCC, MAKEFOURCC('R','G','B','G'), 0, 0, 0, 0, 0);
|
|
public static DDS_PIXELFORMAT DDSPF_G8R8_G8B8 = new DDS_PIXELFORMAT(32, DDS_FOURCC, MAKEFOURCC('G','R','G','B'), 0, 0, 0, 0, 0);
|
|
public static DDS_PIXELFORMAT DDSPF_YUY2 = new DDS_PIXELFORMAT(32, DDS_FOURCC, MAKEFOURCC('Y','U','Y','2'), 0, 0, 0, 0, 0);
|
|
public static DDS_PIXELFORMAT DDSPF_A8R8G8B8 = new DDS_PIXELFORMAT(32, DDS_RGBA, 0, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
|
|
public static DDS_PIXELFORMAT DDSPF_X8R8G8B8 = new DDS_PIXELFORMAT(32, DDS_RGB, 0, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000);
|
|
public static DDS_PIXELFORMAT DDSPF_A8B8G8R8 = new DDS_PIXELFORMAT(32, DDS_RGBA, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
|
|
public static DDS_PIXELFORMAT DDSPF_X8B8G8R8 = new DDS_PIXELFORMAT(32, DDS_RGB, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0x00000000);
|
|
public static DDS_PIXELFORMAT DDSPF_G16R16 = new DDS_PIXELFORMAT(32, DDS_RGB, 0, 32, 0x0000ffff, 0xffff0000, 0x00000000, 0x00000000);
|
|
public static DDS_PIXELFORMAT DDSPF_R5G6B5 = new DDS_PIXELFORMAT(32, DDS_RGB, 0, 16, 0x0000f800, 0x000007e0, 0x0000001f, 0x00000000);
|
|
public static DDS_PIXELFORMAT DDSPF_A1R5G5B5 = new DDS_PIXELFORMAT(32, DDS_RGBA, 0, 16, 0x00007c00, 0x000003e0, 0x0000001f, 0x00008000);
|
|
public static DDS_PIXELFORMAT DDSPF_A4R4G4B4 = new DDS_PIXELFORMAT(32, DDS_RGBA, 0, 16, 0x00000f00, 0x000000f0, 0x0000000f, 0x0000f000);
|
|
public static DDS_PIXELFORMAT DDSPF_R8G8B8 = new DDS_PIXELFORMAT(32, DDS_RGB, 0, 24, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000);
|
|
public static DDS_PIXELFORMAT DDSPF_L8 = new DDS_PIXELFORMAT(32, DDS_LUMINANCE, 0, 8, 0xff, 0x00, 0x00, 0x00);
|
|
public static DDS_PIXELFORMAT DDSPF_L16 = new DDS_PIXELFORMAT(32, DDS_LUMINANCE, 0, 16, 0xffff, 0x0000, 0x0000, 0x0000);
|
|
public static DDS_PIXELFORMAT DDSPF_A8L8 = new DDS_PIXELFORMAT(32, DDS_LUMINANCEA, 0, 16, 0x00ff, 0x0000, 0x0000, 0xff00);
|
|
public static DDS_PIXELFORMAT DDSPF_A8 = new DDS_PIXELFORMAT(32, DDS_ALPHA, 0, 8, 0x00, 0x00, 0x00, 0xff);
|
|
|
|
// D3DFMT_A2R10G10B10/D3DFMT_A2B10G10R10 should be written using DX10 extension to avoid D3DX 10:10:10:2 reversal issue
|
|
|
|
// This indicates the DDS_HEADER_DXT10 extension is present (the format is in dxgiFormat)
|
|
public static DDS_PIXELFORMAT DDSPF_DX10 = new DDS_PIXELFORMAT(32, DDS_FOURCC, MAKEFOURCC('D','X','1','0'), 0, 0, 0, 0, 0);
|
|
|
|
|
|
|
|
|
|
|
|
private bool ISBITMASK(uint r, uint g, uint b, uint a)
|
|
{
|
|
return ((dwRBitMask == r) && (dwGBitMask == g) && (dwBBitMask == b) && (dwABitMask == a));
|
|
}
|
|
|
|
public DXGI_FORMAT GetDXGIFormat()
|
|
{
|
|
|
|
if ((dwFlags & DDS_RGB) > 0)
|
|
{
|
|
// Note that sRGB formats are written using the "DX10" extended header
|
|
|
|
switch (dwRGBBitCount)
|
|
{
|
|
case 32:
|
|
if (ISBITMASK(0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000))
|
|
{
|
|
return DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
}
|
|
|
|
if (ISBITMASK(0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000))
|
|
{
|
|
return DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM;
|
|
}
|
|
|
|
if (ISBITMASK(0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000))
|
|
{
|
|
return DXGI_FORMAT.DXGI_FORMAT_B8G8R8X8_UNORM;
|
|
}
|
|
|
|
// No DXGI format maps to ISBITMASK(0x000000ff, 0x0000ff00, 0x00ff0000, 0x00000000) aka D3DFMT_X8B8G8R8
|
|
|
|
// 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 DXGI_FORMAT_R10G10B10A2_UNORM format directly
|
|
|
|
// For 'correct' writers, this should be 0x000003ff, 0x000ffc00, 0x3ff00000 for RGB data
|
|
if (ISBITMASK(0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000))
|
|
{
|
|
return DXGI_FORMAT.DXGI_FORMAT_R10G10B10A2_UNORM;
|
|
}
|
|
|
|
// No DXGI format maps to ISBITMASK(0x000003ff, 0x000ffc00, 0x3ff00000, 0xc0000000) aka D3DFMT_A2R10G10B10
|
|
|
|
if (ISBITMASK(0x0000ffff, 0xffff0000, 0x00000000, 0x00000000))
|
|
{
|
|
return DXGI_FORMAT.DXGI_FORMAT_R16G16_UNORM;
|
|
}
|
|
|
|
if (ISBITMASK(0xffffffff, 0x00000000, 0x00000000, 0x00000000))
|
|
{
|
|
// Only 32-bit color channel format in D3D9 was R32F
|
|
return DXGI_FORMAT.DXGI_FORMAT_R32_FLOAT; // D3DX writes this out as a FourCC of 114
|
|
}
|
|
break;
|
|
|
|
case 24:
|
|
// No 24bpp DXGI formats aka D3DFMT_R8G8B8
|
|
break;
|
|
|
|
case 16:
|
|
if (ISBITMASK(0x7c00, 0x03e0, 0x001f, 0x8000))
|
|
{
|
|
return DXGI_FORMAT.DXGI_FORMAT_B5G5R5A1_UNORM;
|
|
}
|
|
if (ISBITMASK(0xf800, 0x07e0, 0x001f, 0x0000))
|
|
{
|
|
return DXGI_FORMAT.DXGI_FORMAT_B5G6R5_UNORM;
|
|
}
|
|
|
|
// No DXGI format maps to ISBITMASK(0x7c00, 0x03e0, 0x001f, 0x0000) aka D3DFMT_X1R5G5B5
|
|
if (ISBITMASK(0x0f00, 0x00f0, 0x000f, 0xf000))
|
|
{
|
|
return DXGI_FORMAT.DXGI_FORMAT_B4G4R4A4_UNORM;
|
|
}
|
|
|
|
// No DXGI format maps to ISBITMASK(0x0f00, 0x00f0, 0x000f, 0x0000) aka D3DFMT_X4R4G4B4
|
|
|
|
// No 3:3:2, 3:3:2:8, or paletted DXGI formats aka D3DFMT_A8R3G3B2, D3DFMT_R3G3B2, D3DFMT_P8, D3DFMT_A8P8, etc.
|
|
break;
|
|
}
|
|
}
|
|
else if ((dwFlags & DDS_LUMINANCE) > 0)
|
|
{
|
|
if (8 == dwRGBBitCount)
|
|
{
|
|
if (ISBITMASK(0x000000ff, 0x00000000, 0x00000000, 0x00000000))
|
|
{
|
|
return DXGI_FORMAT.DXGI_FORMAT_R8_UNORM; // D3DX10/11 writes this out as DX10 extension
|
|
}
|
|
|
|
// No DXGI format maps to ISBITMASK(0x0f, 0x00, 0x00, 0xf0) aka D3DFMT_A4L4
|
|
}
|
|
|
|
if (16 == dwRGBBitCount)
|
|
{
|
|
if (ISBITMASK(0x0000ffff, 0x00000000, 0x00000000, 0x00000000))
|
|
{
|
|
return DXGI_FORMAT.DXGI_FORMAT_R16_UNORM; // D3DX10/11 writes this out as DX10 extension
|
|
}
|
|
if (ISBITMASK(0x000000ff, 0x00000000, 0x00000000, 0x0000ff00))
|
|
{
|
|
return DXGI_FORMAT.DXGI_FORMAT_R8G8_UNORM; // D3DX10/11 writes this out as DX10 extension
|
|
}
|
|
}
|
|
}
|
|
else if ((dwFlags & DDS_ALPHA) > 0)
|
|
{
|
|
if (8 == dwRGBBitCount)
|
|
{
|
|
return DXGI_FORMAT.DXGI_FORMAT_A8_UNORM;
|
|
}
|
|
}
|
|
else if ((dwFlags & DDS_FOURCC) > 0)
|
|
{
|
|
if (MAKEFOURCC('D', 'X', 'T', '1') == dwFourCC)
|
|
{
|
|
return DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM;
|
|
}
|
|
if (MAKEFOURCC('D', 'X', 'T', '3') == dwFourCC)
|
|
{
|
|
return DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM;
|
|
}
|
|
if (MAKEFOURCC('D', 'X', 'T', '5') == dwFourCC)
|
|
{
|
|
return DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM;
|
|
}
|
|
|
|
// While pre-mulitplied alpha isn't directly supported by the DXGI formats,
|
|
// they are basically the same as these BC formats so they can be mapped
|
|
if (MAKEFOURCC('D', 'X', 'T', '2') == dwFourCC)
|
|
{
|
|
return DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM;
|
|
}
|
|
if (MAKEFOURCC('D', 'X', 'T', '4') == dwFourCC)
|
|
{
|
|
return DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM;
|
|
}
|
|
|
|
if (MAKEFOURCC('A', 'T', 'I', '1') == dwFourCC)
|
|
{
|
|
return DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM;
|
|
}
|
|
if (MAKEFOURCC('B', 'C', '4', 'U') == dwFourCC)
|
|
{
|
|
return DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM;
|
|
}
|
|
if (MAKEFOURCC('B', 'C', '4', 'S') == dwFourCC)
|
|
{
|
|
return DXGI_FORMAT.DXGI_FORMAT_BC4_SNORM;
|
|
}
|
|
|
|
if (MAKEFOURCC('A', 'T', 'I', '2') == dwFourCC)
|
|
{
|
|
return DXGI_FORMAT.DXGI_FORMAT_BC5_UNORM;
|
|
}
|
|
if (MAKEFOURCC('B', 'C', '5', 'U') == dwFourCC)
|
|
{
|
|
return DXGI_FORMAT.DXGI_FORMAT_BC5_UNORM;
|
|
}
|
|
if (MAKEFOURCC('B', 'C', '5', 'S') == dwFourCC)
|
|
{
|
|
return DXGI_FORMAT.DXGI_FORMAT_BC5_SNORM;
|
|
}
|
|
|
|
// BC6H and BC7 are written using the "DX10" extended header
|
|
|
|
if (MAKEFOURCC('R', 'G', 'B', 'G') == dwFourCC)
|
|
{
|
|
return DXGI_FORMAT.DXGI_FORMAT_R8G8_B8G8_UNORM;
|
|
}
|
|
if (MAKEFOURCC('G', 'R', 'G', 'B') == dwFourCC)
|
|
{
|
|
return DXGI_FORMAT.DXGI_FORMAT_G8R8_G8B8_UNORM;
|
|
}
|
|
|
|
// Check for D3DFORMAT enums being set here
|
|
switch (dwFourCC)
|
|
{
|
|
case 36: // D3DFMT_A16B16G16R16
|
|
return DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_UNORM;
|
|
|
|
case 110: // D3DFMT_Q16W16V16U16
|
|
return DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_SNORM;
|
|
|
|
case 111: // D3DFMT_R16F
|
|
return DXGI_FORMAT.DXGI_FORMAT_R16_FLOAT;
|
|
|
|
case 112: // D3DFMT_G16R16F
|
|
return DXGI_FORMAT.DXGI_FORMAT_R16G16_FLOAT;
|
|
|
|
case 113: // D3DFMT_A16B16G16R16F
|
|
return DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_FLOAT;
|
|
|
|
case 114: // D3DFMT_R32F
|
|
return DXGI_FORMAT.DXGI_FORMAT_R32_FLOAT;
|
|
|
|
case 115: // D3DFMT_G32R32F
|
|
return DXGI_FORMAT.DXGI_FORMAT_R32G32_FLOAT;
|
|
|
|
case 116: // D3DFMT_A32B32G32R32F
|
|
return DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_FLOAT;
|
|
}
|
|
}
|
|
|
|
return DXGI_FORMAT.DXGI_FORMAT_UNKNOWN;
|
|
}
|
|
|
|
|
|
};
|
|
|
|
public enum DDS_FLAGS : uint
|
|
{
|
|
DDS_FLAGS_NONE = 0x0,
|
|
|
|
DDS_FLAGS_LEGACY_DWORD = 0x1,
|
|
// Assume pitch is DWORD aligned instead of BYTE aligned (used by some legacy DDS files)
|
|
|
|
DDS_FLAGS_NO_LEGACY_EXPANSION = 0x2,
|
|
// Do not implicitly convert legacy formats that result in larger pixel sizes (24 bpp, 3:3:2, A8L8, A4L4, P8, A8P8)
|
|
|
|
DDS_FLAGS_NO_R10B10G10A2_FIXUP = 0x4,
|
|
// Do not use work-around for long-standing D3DX DDS file format issue which reversed the 10:10:10:2 color order masks
|
|
|
|
DDS_FLAGS_FORCE_RGB = 0x8,
|
|
// Convert DXGI 1.1 BGR formats to DXGI_FORMAT_R8G8B8A8_UNORM to avoid use of optional WDDM 1.1 formats
|
|
|
|
DDS_FLAGS_NO_16BPP = 0x10,
|
|
// Conversions avoid use of 565, 5551, and 4444 formats and instead expand to 8888 to avoid use of optional WDDM 1.2 formats
|
|
|
|
DDS_FLAGS_EXPAND_LUMINANCE = 0x20,
|
|
// When loading legacy luminance formats expand replicating the color channels rather than leaving them packed (L8, L16, A8L8)
|
|
|
|
DDS_FLAGS_FORCE_DX10_EXT = 0x10000,
|
|
// Always use the 'DX10' header extension for DDS writer (i.e. don't try to write DX9 compatible DDS files)
|
|
|
|
DDS_FLAGS_FORCE_DX10_EXT_MISC2 = 0x20000,
|
|
// DDS_FLAGS_FORCE_DX10_EXT including miscFlags2 information (result may not be compatible with D3DX10 or D3DX11)
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
internal static byte[] DecompressDxt1(byte[] imageData, int width, int height)
|
|
{
|
|
using (MemoryStream imageStream = new MemoryStream(imageData))
|
|
return DecompressDxt1(imageStream, width, height);
|
|
}
|
|
|
|
internal static byte[] DecompressDxt1(Stream imageStream, int width, int height)
|
|
{
|
|
byte[] imageData = new byte[width * height * 4];
|
|
|
|
using (BinaryReader imageReader = new BinaryReader(imageStream))
|
|
{
|
|
int blockCountX = (width + 3) / 4;
|
|
int blockCountY = (height + 3) / 4;
|
|
|
|
for (int y = 0; y < blockCountY; y++)
|
|
{
|
|
for (int x = 0; x < blockCountX; x++)
|
|
{
|
|
DecompressDxt1Block(imageReader, x, y, blockCountX, width, height, imageData);
|
|
}
|
|
}
|
|
}
|
|
|
|
return imageData;
|
|
}
|
|
|
|
private static void DecompressDxt1Block(BinaryReader imageReader, int x, int y, int blockCountX, int width, int height, byte[] imageData)
|
|
{
|
|
ushort c0 = imageReader.ReadUInt16();
|
|
ushort c1 = imageReader.ReadUInt16();
|
|
|
|
byte r0, g0, b0;
|
|
byte r1, g1, b1;
|
|
ConvertRgb565ToRgb888(c0, out r0, out g0, out b0);
|
|
ConvertRgb565ToRgb888(c1, out r1, out g1, out b1);
|
|
|
|
uint lookupTable = imageReader.ReadUInt32();
|
|
|
|
for (int blockY = 0; blockY < 4; blockY++)
|
|
{
|
|
for (int blockX = 0; blockX < 4; blockX++)
|
|
{
|
|
byte r = 0, g = 0, b = 0, a = 255;
|
|
uint index = (lookupTable >> 2 * (4 * blockY + blockX)) & 0x03;
|
|
|
|
if (c0 > c1)
|
|
{
|
|
switch (index)
|
|
{
|
|
case 0:
|
|
r = r0;
|
|
g = g0;
|
|
b = b0;
|
|
break;
|
|
case 1:
|
|
r = r1;
|
|
g = g1;
|
|
b = b1;
|
|
break;
|
|
case 2:
|
|
r = (byte)((2 * r0 + r1) / 3);
|
|
g = (byte)((2 * g0 + g1) / 3);
|
|
b = (byte)((2 * b0 + b1) / 3);
|
|
break;
|
|
case 3:
|
|
r = (byte)((r0 + 2 * r1) / 3);
|
|
g = (byte)((g0 + 2 * g1) / 3);
|
|
b = (byte)((b0 + 2 * b1) / 3);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (index)
|
|
{
|
|
case 0:
|
|
r = r0;
|
|
g = g0;
|
|
b = b0;
|
|
break;
|
|
case 1:
|
|
r = r1;
|
|
g = g1;
|
|
b = b1;
|
|
break;
|
|
case 2:
|
|
r = (byte)((r0 + r1) / 2);
|
|
g = (byte)((g0 + g1) / 2);
|
|
b = (byte)((b0 + b1) / 2);
|
|
break;
|
|
case 3:
|
|
r = 0;
|
|
g = 0;
|
|
b = 0;
|
|
a = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
int px = (x << 2) + blockX;
|
|
int py = (y << 2) + blockY;
|
|
if ((px < width) && (py < height))
|
|
{
|
|
int offset = ((py * width) + px) << 2;
|
|
imageData[offset] = r;
|
|
imageData[offset + 1] = g;
|
|
imageData[offset + 2] = b;
|
|
imageData[offset + 3] = a;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
internal static byte[] DecompressDxt3(byte[] imageData, int width, int height)
|
|
{
|
|
using (MemoryStream imageStream = new MemoryStream(imageData))
|
|
return DecompressDxt3(imageStream, width, height);
|
|
}
|
|
|
|
internal static byte[] DecompressDxt3(Stream imageStream, int width, int height)
|
|
{
|
|
byte[] imageData = new byte[width * height * 4];
|
|
|
|
using (BinaryReader imageReader = new BinaryReader(imageStream))
|
|
{
|
|
int blockCountX = (width + 3) / 4;
|
|
int blockCountY = (height + 3) / 4;
|
|
|
|
for (int y = 0; y < blockCountY; y++)
|
|
{
|
|
for (int x = 0; x < blockCountX; x++)
|
|
{
|
|
DecompressDxt3Block(imageReader, x, y, blockCountX, width, height, imageData);
|
|
}
|
|
}
|
|
}
|
|
|
|
return imageData;
|
|
}
|
|
|
|
private static void DecompressDxt3Block(BinaryReader imageReader, int x, int y, int blockCountX, int width, int height, byte[] imageData)
|
|
{
|
|
byte a0 = imageReader.ReadByte();
|
|
byte a1 = imageReader.ReadByte();
|
|
byte a2 = imageReader.ReadByte();
|
|
byte a3 = imageReader.ReadByte();
|
|
byte a4 = imageReader.ReadByte();
|
|
byte a5 = imageReader.ReadByte();
|
|
byte a6 = imageReader.ReadByte();
|
|
byte a7 = imageReader.ReadByte();
|
|
|
|
ushort c0 = imageReader.ReadUInt16();
|
|
ushort c1 = imageReader.ReadUInt16();
|
|
|
|
byte r0, g0, b0;
|
|
byte r1, g1, b1;
|
|
ConvertRgb565ToRgb888(c0, out r0, out g0, out b0);
|
|
ConvertRgb565ToRgb888(c1, out r1, out g1, out b1);
|
|
|
|
uint lookupTable = imageReader.ReadUInt32();
|
|
|
|
int alphaIndex = 0;
|
|
for (int blockY = 0; blockY < 4; blockY++)
|
|
{
|
|
for (int blockX = 0; blockX < 4; blockX++)
|
|
{
|
|
byte r = 0, g = 0, b = 0, a = 0;
|
|
|
|
uint index = (lookupTable >> 2 * (4 * blockY + blockX)) & 0x03;
|
|
|
|
switch (alphaIndex)
|
|
{
|
|
case 0:
|
|
a = (byte)((a0 & 0x0F) | ((a0 & 0x0F) << 4));
|
|
break;
|
|
case 1:
|
|
a = (byte)((a0 & 0xF0) | ((a0 & 0xF0) >> 4));
|
|
break;
|
|
case 2:
|
|
a = (byte)((a1 & 0x0F) | ((a1 & 0x0F) << 4));
|
|
break;
|
|
case 3:
|
|
a = (byte)((a1 & 0xF0) | ((a1 & 0xF0) >> 4));
|
|
break;
|
|
case 4:
|
|
a = (byte)((a2 & 0x0F) | ((a2 & 0x0F) << 4));
|
|
break;
|
|
case 5:
|
|
a = (byte)((a2 & 0xF0) | ((a2 & 0xF0) >> 4));
|
|
break;
|
|
case 6:
|
|
a = (byte)((a3 & 0x0F) | ((a3 & 0x0F) << 4));
|
|
break;
|
|
case 7:
|
|
a = (byte)((a3 & 0xF0) | ((a3 & 0xF0) >> 4));
|
|
break;
|
|
case 8:
|
|
a = (byte)((a4 & 0x0F) | ((a4 & 0x0F) << 4));
|
|
break;
|
|
case 9:
|
|
a = (byte)((a4 & 0xF0) | ((a4 & 0xF0) >> 4));
|
|
break;
|
|
case 10:
|
|
a = (byte)((a5 & 0x0F) | ((a5 & 0x0F) << 4));
|
|
break;
|
|
case 11:
|
|
a = (byte)((a5 & 0xF0) | ((a5 & 0xF0) >> 4));
|
|
break;
|
|
case 12:
|
|
a = (byte)((a6 & 0x0F) | ((a6 & 0x0F) << 4));
|
|
break;
|
|
case 13:
|
|
a = (byte)((a6 & 0xF0) | ((a6 & 0xF0) >> 4));
|
|
break;
|
|
case 14:
|
|
a = (byte)((a7 & 0x0F) | ((a7 & 0x0F) << 4));
|
|
break;
|
|
case 15:
|
|
a = (byte)((a7 & 0xF0) | ((a7 & 0xF0) >> 4));
|
|
break;
|
|
}
|
|
++alphaIndex;
|
|
|
|
switch (index)
|
|
{
|
|
case 0:
|
|
r = r0;
|
|
g = g0;
|
|
b = b0;
|
|
break;
|
|
case 1:
|
|
r = r1;
|
|
g = g1;
|
|
b = b1;
|
|
break;
|
|
case 2:
|
|
r = (byte)((2 * r0 + r1) / 3);
|
|
g = (byte)((2 * g0 + g1) / 3);
|
|
b = (byte)((2 * b0 + b1) / 3);
|
|
break;
|
|
case 3:
|
|
r = (byte)((r0 + 2 * r1) / 3);
|
|
g = (byte)((g0 + 2 * g1) / 3);
|
|
b = (byte)((b0 + 2 * b1) / 3);
|
|
break;
|
|
}
|
|
|
|
int px = (x << 2) + blockX;
|
|
int py = (y << 2) + blockY;
|
|
if ((px < width) && (py < height))
|
|
{
|
|
int offset = ((py * width) + px) << 2;
|
|
imageData[offset] = r;
|
|
imageData[offset + 1] = g;
|
|
imageData[offset + 2] = b;
|
|
imageData[offset + 3] = a;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
internal static byte[] DecompressDxt5(byte[] imageData, int width, int height)
|
|
{
|
|
using (MemoryStream imageStream = new MemoryStream(imageData))
|
|
return DecompressDxt5(imageStream, width, height);
|
|
}
|
|
|
|
internal static byte[] DecompressDxt5(Stream imageStream, int width, int height)
|
|
{
|
|
byte[] imageData = new byte[width * height * 4];
|
|
|
|
using (BinaryReader imageReader = new BinaryReader(imageStream))
|
|
{
|
|
int blockCountX = (width + 3) / 4;
|
|
int blockCountY = (height + 3) / 4;
|
|
|
|
for (int y = 0; y < blockCountY; y++)
|
|
{
|
|
for (int x = 0; x < blockCountX; x++)
|
|
{
|
|
DecompressDxt5Block(imageReader, x, y, blockCountX, width, height, imageData);
|
|
}
|
|
}
|
|
}
|
|
|
|
return imageData;
|
|
}
|
|
|
|
private static void DecompressDxt5Block(BinaryReader imageReader, int x, int y, int blockCountX, int width, int height, byte[] imageData)
|
|
{
|
|
byte alpha0 = imageReader.ReadByte();
|
|
byte alpha1 = imageReader.ReadByte();
|
|
|
|
ulong alphaMask = (ulong)imageReader.ReadByte();
|
|
alphaMask += (ulong)imageReader.ReadByte() << 8;
|
|
alphaMask += (ulong)imageReader.ReadByte() << 16;
|
|
alphaMask += (ulong)imageReader.ReadByte() << 24;
|
|
alphaMask += (ulong)imageReader.ReadByte() << 32;
|
|
alphaMask += (ulong)imageReader.ReadByte() << 40;
|
|
|
|
ushort c0 = imageReader.ReadUInt16();
|
|
ushort c1 = imageReader.ReadUInt16();
|
|
|
|
byte r0, g0, b0;
|
|
byte r1, g1, b1;
|
|
ConvertRgb565ToRgb888(c0, out r0, out g0, out b0);
|
|
ConvertRgb565ToRgb888(c1, out r1, out g1, out b1);
|
|
|
|
uint lookupTable = imageReader.ReadUInt32();
|
|
|
|
for (int blockY = 0; blockY < 4; blockY++)
|
|
{
|
|
for (int blockX = 0; blockX < 4; blockX++)
|
|
{
|
|
byte r = 0, g = 0, b = 0, a = 255;
|
|
uint index = (lookupTable >> 2 * (4 * blockY + blockX)) & 0x03;
|
|
|
|
uint alphaIndex = (uint)((alphaMask >> 3 * (4 * blockY + blockX)) & 0x07);
|
|
if (alphaIndex == 0)
|
|
{
|
|
a = alpha0;
|
|
}
|
|
else if (alphaIndex == 1)
|
|
{
|
|
a = alpha1;
|
|
}
|
|
else if (alpha0 > alpha1)
|
|
{
|
|
a = (byte)(((8 - alphaIndex) * alpha0 + (alphaIndex - 1) * alpha1) / 7);
|
|
}
|
|
else if (alphaIndex == 6)
|
|
{
|
|
a = 0;
|
|
}
|
|
else if (alphaIndex == 7)
|
|
{
|
|
a = 0xff;
|
|
}
|
|
else
|
|
{
|
|
a = (byte)(((6 - alphaIndex) * alpha0 + (alphaIndex - 1) * alpha1) / 5);
|
|
}
|
|
|
|
switch (index)
|
|
{
|
|
case 0:
|
|
r = r0;
|
|
g = g0;
|
|
b = b0;
|
|
break;
|
|
case 1:
|
|
r = r1;
|
|
g = g1;
|
|
b = b1;
|
|
break;
|
|
case 2:
|
|
r = (byte)((2 * r0 + r1) / 3);
|
|
g = (byte)((2 * g0 + g1) / 3);
|
|
b = (byte)((2 * b0 + b1) / 3);
|
|
break;
|
|
case 3:
|
|
r = (byte)((r0 + 2 * r1) / 3);
|
|
g = (byte)((g0 + 2 * g1) / 3);
|
|
b = (byte)((b0 + 2 * b1) / 3);
|
|
break;
|
|
}
|
|
|
|
int px = (x << 2) + blockX;
|
|
int py = (y << 2) + blockY;
|
|
if ((px < width) && (py < height))
|
|
{
|
|
int offset = ((py * width) + px) << 2;
|
|
imageData[offset] = r;
|
|
imageData[offset + 1] = g;
|
|
imageData[offset + 2] = b;
|
|
imageData[offset + 3] = a;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void ConvertRgb565ToRgb888(ushort color, out byte r, out byte g, out byte b)
|
|
{
|
|
int temp;
|
|
temp = (color >> 11) * 255 + 16;
|
|
r = (byte)((temp / 32 + temp) / 32);
|
|
temp = ((color & 0x07E0) >> 5) * 255 + 32;
|
|
g = (byte)((temp / 64 + temp) / 64);
|
|
temp = (color & 0x001F) * 255 + 16;
|
|
b = (byte)((temp / 32 + temp) / 32);
|
|
}
|
|
|
|
private static void ConvertBgra5551ToRgba8(ushort color, out byte r, out byte g, out byte b, out byte a)
|
|
{
|
|
//added by dexy
|
|
int temp;
|
|
temp = ((color >> 11) & 0x1F) * 255 + 16;
|
|
r = (byte)((temp / 32 + temp) / 32);
|
|
temp = ((color >> 6) & 0x1F) * 255 + 32;
|
|
g = (byte)((temp / 32 + temp) / 32);
|
|
temp = ((color >> 1) & 0x1F) * 255 + 16;
|
|
b = (byte)((temp / 32 + temp) / 32);
|
|
a = (byte)((color & 1) * 255);
|
|
}
|
|
|
|
|
|
|
|
|
|
//these ones added by dexy - based on DecompressDxt
|
|
|
|
internal static byte[] DecompressBC4(byte[] imageData, int width, int height)
|
|
{
|
|
using (MemoryStream imageStream = new MemoryStream(imageData))
|
|
return DecompressBC4(imageStream, width, height);
|
|
}
|
|
|
|
internal static byte[] DecompressBC4(Stream imageStream, int width, int height)
|
|
{
|
|
byte[] imageData = new byte[width * height * 4];
|
|
|
|
using (BinaryReader imageReader = new BinaryReader(imageStream))
|
|
{
|
|
int blockCountX = (width + 3) / 4;
|
|
int blockCountY = (height + 3) / 4;
|
|
|
|
for (int y = 0; y < blockCountY; y++)
|
|
{
|
|
for (int x = 0; x < blockCountX; x++)
|
|
{
|
|
DecompressBC4Block(imageReader, x, y, blockCountX, width, height, imageData);
|
|
}
|
|
}
|
|
}
|
|
|
|
return imageData;
|
|
}
|
|
|
|
private static void DecompressBC4Block(BinaryReader imageReader, int x, int y, int blockCountX, int width, int height, byte[] imageData)
|
|
{
|
|
byte r0 = imageReader.ReadByte();
|
|
byte r1 = imageReader.ReadByte();
|
|
|
|
ulong rMask = (ulong)imageReader.ReadByte();
|
|
rMask += (ulong)imageReader.ReadByte() << 8;
|
|
rMask += (ulong)imageReader.ReadByte() << 16;
|
|
rMask += (ulong)imageReader.ReadByte() << 24;
|
|
rMask += (ulong)imageReader.ReadByte() << 32;
|
|
rMask += (ulong)imageReader.ReadByte() << 40;
|
|
|
|
for (int blockY = 0; blockY < 4; blockY++)
|
|
{
|
|
for (int blockX = 0; blockX < 4; blockX++)
|
|
{
|
|
byte r = 255;
|
|
|
|
uint rIndex = (uint)((rMask >> 3 * (4 * blockY + blockX)) & 0x07);
|
|
if (rIndex == 0)
|
|
{
|
|
r = r0;
|
|
}
|
|
else if (rIndex == 1)
|
|
{
|
|
r = r1;
|
|
}
|
|
else if (r0 > r1)
|
|
{
|
|
r = (byte)(((8 - rIndex) * r0 + (rIndex - 1) * r1) / 7);
|
|
}
|
|
else if (rIndex == 6)
|
|
{
|
|
r = 0;
|
|
}
|
|
else if (rIndex == 7)
|
|
{
|
|
r = 0xff;
|
|
}
|
|
else
|
|
{
|
|
r = (byte)(((6 - rIndex) * r0 + (rIndex - 1) * r1) / 5);
|
|
}
|
|
|
|
int px = (x << 2) + blockX;
|
|
int py = (y << 2) + blockY;
|
|
if ((px < width) && (py < height))
|
|
{
|
|
int offset = ((py * width) + px) << 2;
|
|
imageData[offset] = r;
|
|
imageData[offset + 1] = 0;
|
|
imageData[offset + 2] = 0;
|
|
imageData[offset + 3] = 255;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
internal static byte[] DecompressBC5(byte[] imageData, int width, int height)
|
|
{
|
|
using (MemoryStream imageStream = new MemoryStream(imageData))
|
|
return DecompressBC5(imageStream, width, height);
|
|
}
|
|
|
|
internal static byte[] DecompressBC5(Stream imageStream, int width, int height)
|
|
{
|
|
byte[] imageData = new byte[width * height * 4];
|
|
|
|
using (BinaryReader imageReader = new BinaryReader(imageStream))
|
|
{
|
|
int blockCountX = (width + 3) / 4;
|
|
int blockCountY = (height + 3) / 4;
|
|
|
|
for (int y = 0; y < blockCountY; y++)
|
|
{
|
|
for (int x = 0; x < blockCountX; x++)
|
|
{
|
|
DecompressBC5Block(imageReader, x, y, blockCountX, width, height, imageData);
|
|
}
|
|
}
|
|
}
|
|
|
|
return imageData;
|
|
}
|
|
|
|
private static void DecompressBC5Block(BinaryReader imageReader, int x, int y, int blockCountX, int width, int height, byte[] imageData)
|
|
{
|
|
byte r0 = imageReader.ReadByte();
|
|
byte r1 = imageReader.ReadByte();
|
|
|
|
ulong rMask = (ulong)imageReader.ReadByte();
|
|
rMask += (ulong)imageReader.ReadByte() << 8;
|
|
rMask += (ulong)imageReader.ReadByte() << 16;
|
|
rMask += (ulong)imageReader.ReadByte() << 24;
|
|
rMask += (ulong)imageReader.ReadByte() << 32;
|
|
rMask += (ulong)imageReader.ReadByte() << 40;
|
|
|
|
byte g0 = imageReader.ReadByte();
|
|
byte g1 = imageReader.ReadByte();
|
|
|
|
ulong gMask = (ulong)imageReader.ReadByte();
|
|
gMask += (ulong)imageReader.ReadByte() << 8;
|
|
gMask += (ulong)imageReader.ReadByte() << 16;
|
|
gMask += (ulong)imageReader.ReadByte() << 24;
|
|
gMask += (ulong)imageReader.ReadByte() << 32;
|
|
gMask += (ulong)imageReader.ReadByte() << 40;
|
|
|
|
for (int blockY = 0; blockY < 4; blockY++)
|
|
{
|
|
for (int blockX = 0; blockX < 4; blockX++)
|
|
{
|
|
byte r = 255;
|
|
uint rIndex = (uint)((rMask >> 3 * (4 * blockY + blockX)) & 0x07);
|
|
if (rIndex == 0)
|
|
{
|
|
r = r0;
|
|
}
|
|
else if (rIndex == 1)
|
|
{
|
|
r = r1;
|
|
}
|
|
else if (r0 > r1)
|
|
{
|
|
r = (byte)(((8 - rIndex) * r0 + (rIndex - 1) * r1) / 7);
|
|
}
|
|
else if (rIndex == 6)
|
|
{
|
|
r = 0;
|
|
}
|
|
else if (rIndex == 7)
|
|
{
|
|
r = 0xff;
|
|
}
|
|
else
|
|
{
|
|
r = (byte)(((6 - rIndex) * r0 + (rIndex - 1) * r1) / 5);
|
|
}
|
|
|
|
|
|
byte g = 255;
|
|
uint gIndex = (uint)((gMask >> 3 * (4 * blockY + blockX)) & 0x07);
|
|
if (gIndex == 0)
|
|
{
|
|
r = r0;
|
|
}
|
|
else if (gIndex == 1)
|
|
{
|
|
r = r1;
|
|
}
|
|
else if (r0 > r1)
|
|
{
|
|
r = (byte)(((8 - gIndex) * r0 + (gIndex - 1) * r1) / 7);
|
|
}
|
|
else if (gIndex == 6)
|
|
{
|
|
r = 0;
|
|
}
|
|
else if (gIndex == 7)
|
|
{
|
|
r = 0xff;
|
|
}
|
|
else
|
|
{
|
|
r = (byte)(((6 - gIndex) * r0 + (gIndex - 1) * r1) / 5);
|
|
}
|
|
|
|
|
|
int px = (x << 2) + blockX;
|
|
int py = (y << 2) + blockY;
|
|
if ((px < width) && (py < height))
|
|
{
|
|
int offset = ((py * width) + px) << 2;
|
|
imageData[offset] = r;
|
|
imageData[offset + 1] = g;
|
|
imageData[offset + 2] = 0;
|
|
imageData[offset + 3] = 255;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private static byte[] ConvertA8ToRGBA8(byte[] imgdata, int w, int h)
|
|
{
|
|
var px = new byte[w * h * 4];
|
|
var pxmax = px.Length - 3;
|
|
for (int i = 0; i < imgdata.Length; i++)
|
|
{
|
|
byte av = imgdata[i];
|
|
int pxi = i * 4;
|
|
if (pxi < pxmax)
|
|
{
|
|
px[pxi] = av;
|
|
px[pxi + 1] = av;
|
|
px[pxi + 2] = av;
|
|
px[pxi + 3] = 255;
|
|
}
|
|
}
|
|
return px;
|
|
}
|
|
private static byte[] ConvertR8ToRGBA8(byte[] imgdata, int w, int h)
|
|
{
|
|
var px = new byte[w * h * 4];
|
|
var pxmax = px.Length - 3;
|
|
for (int i = 0; i < imgdata.Length; i++)
|
|
{
|
|
byte av = imgdata[i];
|
|
int pxi = i * 4;
|
|
if (pxi < pxmax)
|
|
{
|
|
px[pxi] = av;
|
|
px[pxi + 1] = 0;
|
|
px[pxi + 2] = 0;
|
|
px[pxi + 3] = 255;
|
|
}
|
|
}
|
|
return px;
|
|
}
|
|
private static byte[] ConvertBGR5A1ToRGBA8(byte[] imgdata, int w, int h)
|
|
{
|
|
var px = new byte[w * h * 4];
|
|
var pxmax = px.Length - 3;
|
|
for (int i = 0; i < imgdata.Length; i+=2)
|
|
{
|
|
byte v0 = imgdata[i];
|
|
byte v1 = imgdata[i + 1];
|
|
ushort vu = (ushort)(v1 * 255 + v0);
|
|
int pxi = i * 4;
|
|
if (pxi < pxmax)
|
|
{
|
|
ConvertBgra5551ToRgba8(vu, out px[pxi], out px[pxi + 1], out px[pxi + 2], out px[pxi + 3]);
|
|
}
|
|
}
|
|
return px;
|
|
}
|
|
|
|
}
|
|
}
|