CodeWalker/Utils/DDSIO.cs
2017-09-21 20:33:05 +10:00

2412 lines
102 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
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;
}
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
//-------------------------------------------------------------------------------------
// 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;
const uint DDS_MAGIC = 0x20534444; // "DDS "
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 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);
};
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;
}
}
}