1
0
mirror of https://github.com/microsoft/DirectXTex synced 2024-09-18 22:59:54 +00:00
DirectXTex/Texdiag/texdiag.cpp

3989 lines
176 KiB
C++

//--------------------------------------------------------------------------------------
// File: Texdiag.cpp
//
// DirectX Texture diagnostic tool
//
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
//
// http://go.microsoft.com/fwlink/?LinkId=248926
//--------------------------------------------------------------------------------------
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4005)
#endif
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#define NODRAWTEXT
#define NOGDI
#define NOMCX
#define NOSERVICE
#define NOHELP
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#if __cplusplus < 201703L
#error Requires C++17 (and /Zc:__cplusplus with MSVC)
#endif
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cwchar>
#include <cwctype>
#include <filesystem>
#include <fstream>
#include <iterator>
#include <list>
#include <locale>
#include <memory>
#include <new>
#include <string>
#include <tuple>
#include <vector>
#include <dxgiformat.h>
#ifdef _MSC_VER
#pragma warning(disable : 4619 4616 26812)
#endif
#include "DirectXTex.h"
#include <DirectXPackedVector.h>
#ifdef USE_OPENEXR
// See <https://github.com/Microsoft/DirectXTex/wiki/Adding-OpenEXR> for details
#include "DirectXTexEXR.h"
#endif
// See <https://github.com/Microsoft/DirectXTex/wiki/Using-JPEG-PNG-OSS> for details
#ifdef USE_LIBJPEG
#include "DirectXTexJPEG.h"
#endif
#ifdef USE_LIBPNG
#include "DirectXTexPNG.h"
#endif
#define TOOL_VERSION DIRECTX_TEX_VERSION
#include "CmdLineHelpers.h"
using namespace Helpers;
using namespace DirectX;
namespace
{
const wchar_t* g_ToolName = L"texdiag";
const wchar_t* g_Description = L"Microsoft (R) DirectX Texture Diagnostic Tool [DirectXTex]";
enum COMMANDS : uint32_t
{
CMD_INFO = 1,
CMD_ANALYZE,
CMD_COMPARE,
CMD_DIFF,
CMD_DUMPBC,
CMD_DUMPDDS,
CMD_MAX
};
enum OPTIONS : uint32_t
{
OPT_RECURSIVE = 1,
OPT_FORMAT,
OPT_FILTER,
OPT_DDS_DWORD_ALIGN,
OPT_DDS_BAD_DXTN_TAILS,
OPT_DDS_PERMISSIVE,
OPT_DDS_IGNORE_MIPS,
OPT_OUTPUTFILE,
OPT_TOLOWER,
OPT_OVERWRITE,
OPT_FILETYPE,
OPT_NOLOGO,
OPT_TYPELESS_UNORM,
OPT_TYPELESS_FLOAT,
OPT_EXPAND_LUMINANCE,
OPT_TARGET_PIXELX,
OPT_TARGET_PIXELY,
OPT_DIFF_COLOR,
OPT_THRESHOLD,
OPT_FILELIST,
OPT_MAX
};
static_assert(OPT_MAX <= 32, "dwOptions is a unsigned int bitfield");
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
const SValue<uint32_t> g_pCommands[] =
{
{ L"info", CMD_INFO },
{ L"analyze", CMD_ANALYZE },
{ L"compare", CMD_COMPARE },
{ L"diff", CMD_DIFF },
{ L"dumpbc", CMD_DUMPBC },
{ L"dumpdds", CMD_DUMPDDS },
{ nullptr, 0 }
};
const SValue<uint32_t> g_pOptions[] =
{
{ L"r", OPT_RECURSIVE },
{ L"f", OPT_FORMAT },
{ L"if", OPT_FILTER },
{ L"dword", OPT_DDS_DWORD_ALIGN },
{ L"badtails", OPT_DDS_BAD_DXTN_TAILS },
{ L"permissive", OPT_DDS_PERMISSIVE },
{ L"ignoremips", OPT_DDS_IGNORE_MIPS },
{ L"nologo", OPT_NOLOGO },
{ L"o", OPT_OUTPUTFILE },
{ L"l", OPT_TOLOWER },
{ L"y", OPT_OVERWRITE },
{ L"ft", OPT_FILETYPE },
{ L"tu", OPT_TYPELESS_UNORM },
{ L"tf", OPT_TYPELESS_FLOAT },
{ L"xlum", OPT_EXPAND_LUMINANCE },
{ L"targetx", OPT_TARGET_PIXELX },
{ L"targety", OPT_TARGET_PIXELY },
{ L"c", OPT_DIFF_COLOR },
{ L"t", OPT_THRESHOLD },
{ L"flist", OPT_FILELIST },
{ nullptr, 0 }
};
#define DEFFMT(fmt) { L## #fmt, DXGI_FORMAT_ ## fmt }
const SValue<DXGI_FORMAT> g_pFormats[] =
{
// List does not include _TYPELESS, depth/stencil, or BC formats
DEFFMT(R32G32B32A32_FLOAT),
DEFFMT(R32G32B32A32_UINT),
DEFFMT(R32G32B32A32_SINT),
DEFFMT(R32G32B32_FLOAT),
DEFFMT(R32G32B32_UINT),
DEFFMT(R32G32B32_SINT),
DEFFMT(R16G16B16A16_FLOAT),
DEFFMT(R16G16B16A16_UNORM),
DEFFMT(R16G16B16A16_UINT),
DEFFMT(R16G16B16A16_SNORM),
DEFFMT(R16G16B16A16_SINT),
DEFFMT(R32G32_FLOAT),
DEFFMT(R32G32_UINT),
DEFFMT(R32G32_SINT),
DEFFMT(R10G10B10A2_UNORM),
DEFFMT(R10G10B10A2_UINT),
DEFFMT(R11G11B10_FLOAT),
DEFFMT(R8G8B8A8_UNORM),
DEFFMT(R8G8B8A8_UNORM_SRGB),
DEFFMT(R8G8B8A8_UINT),
DEFFMT(R8G8B8A8_SNORM),
DEFFMT(R8G8B8A8_SINT),
DEFFMT(R16G16_FLOAT),
DEFFMT(R16G16_UNORM),
DEFFMT(R16G16_UINT),
DEFFMT(R16G16_SNORM),
DEFFMT(R16G16_SINT),
DEFFMT(R32_FLOAT),
DEFFMT(R32_UINT),
DEFFMT(R32_SINT),
DEFFMT(R8G8_UNORM),
DEFFMT(R8G8_UINT),
DEFFMT(R8G8_SNORM),
DEFFMT(R8G8_SINT),
DEFFMT(R16_FLOAT),
DEFFMT(R16_UNORM),
DEFFMT(R16_UINT),
DEFFMT(R16_SNORM),
DEFFMT(R16_SINT),
DEFFMT(R8_UNORM),
DEFFMT(R8_UINT),
DEFFMT(R8_SNORM),
DEFFMT(R8_SINT),
DEFFMT(A8_UNORM),
DEFFMT(R9G9B9E5_SHAREDEXP),
DEFFMT(R8G8_B8G8_UNORM),
DEFFMT(G8R8_G8B8_UNORM),
DEFFMT(B5G6R5_UNORM),
DEFFMT(B5G5R5A1_UNORM),
// DXGI 1.1 formats
DEFFMT(B8G8R8A8_UNORM),
DEFFMT(B8G8R8X8_UNORM),
DEFFMT(R10G10B10_XR_BIAS_A2_UNORM),
DEFFMT(B8G8R8A8_UNORM_SRGB),
DEFFMT(B8G8R8X8_UNORM_SRGB),
// DXGI 1.2 formats
DEFFMT(AYUV),
DEFFMT(Y410),
DEFFMT(Y416),
DEFFMT(YUY2),
DEFFMT(Y210),
DEFFMT(Y216),
DEFFMT(B4G4R4A4_UNORM),
// D3D11on12 format
{ L"A4B4G4R4_UNORM", DXGI_FORMAT(191) },
{ nullptr, DXGI_FORMAT_UNKNOWN }
};
const SValue<DXGI_FORMAT> g_pFormatAliases[] =
{
{ L"RGBA", DXGI_FORMAT_R8G8B8A8_UNORM },
{ L"BGRA", DXGI_FORMAT_B8G8R8A8_UNORM },
{ L"BGR", DXGI_FORMAT_B8G8R8X8_UNORM },
{ L"FP16", DXGI_FORMAT_R16G16B16A16_FLOAT },
{ L"FP32", DXGI_FORMAT_R32G32B32A32_FLOAT },
{ nullptr, DXGI_FORMAT_UNKNOWN }
};
const SValue<DXGI_FORMAT> g_pReadOnlyFormats[] =
{
DEFFMT(R32G32B32A32_TYPELESS),
DEFFMT(R32G32B32_TYPELESS),
DEFFMT(R16G16B16A16_TYPELESS),
DEFFMT(R32G32_TYPELESS),
DEFFMT(R32G8X24_TYPELESS),
DEFFMT(D32_FLOAT_S8X24_UINT),
DEFFMT(R32_FLOAT_X8X24_TYPELESS),
DEFFMT(X32_TYPELESS_G8X24_UINT),
DEFFMT(R10G10B10A2_TYPELESS),
DEFFMT(R8G8B8A8_TYPELESS),
DEFFMT(R16G16_TYPELESS),
DEFFMT(R32_TYPELESS),
DEFFMT(D32_FLOAT),
DEFFMT(R24G8_TYPELESS),
DEFFMT(D24_UNORM_S8_UINT),
DEFFMT(R24_UNORM_X8_TYPELESS),
DEFFMT(X24_TYPELESS_G8_UINT),
DEFFMT(R8G8_TYPELESS),
DEFFMT(R16_TYPELESS),
DEFFMT(R8_TYPELESS),
DEFFMT(BC1_TYPELESS),
DEFFMT(BC1_UNORM),
DEFFMT(BC1_UNORM_SRGB),
DEFFMT(BC2_TYPELESS),
DEFFMT(BC2_UNORM),
DEFFMT(BC2_UNORM_SRGB),
DEFFMT(BC3_TYPELESS),
DEFFMT(BC3_UNORM),
DEFFMT(BC3_UNORM_SRGB),
DEFFMT(BC4_TYPELESS),
DEFFMT(BC4_UNORM),
DEFFMT(BC4_SNORM),
DEFFMT(BC5_TYPELESS),
DEFFMT(BC5_UNORM),
DEFFMT(BC5_SNORM),
// DXGI 1.1 formats
DEFFMT(B8G8R8A8_TYPELESS),
DEFFMT(B8G8R8X8_TYPELESS),
DEFFMT(BC6H_TYPELESS),
DEFFMT(BC6H_UF16),
DEFFMT(BC6H_SF16),
DEFFMT(BC7_TYPELESS),
DEFFMT(BC7_UNORM),
DEFFMT(BC7_UNORM_SRGB),
// DXGI 1.2 formats
DEFFMT(AI44),
DEFFMT(IA44),
DEFFMT(P8),
DEFFMT(A8P8),
DEFFMT(NV12),
DEFFMT(P010),
DEFFMT(P016),
DEFFMT(420_OPAQUE),
DEFFMT(NV11),
// DXGI 1.3 formats
{ L"P208", DXGI_FORMAT(130) },
{ L"V208", DXGI_FORMAT(131) },
{ L"V408", DXGI_FORMAT(132) },
// Xbox-specific formats
{ L"R10G10B10_7E3_A2_FLOAT (Xbox)", DXGI_FORMAT(116) },
{ L"R10G10B10_6E4_A2_FLOAT (Xbox)", DXGI_FORMAT(117) },
{ L"D16_UNORM_S8_UINT (Xbox)", DXGI_FORMAT(118) },
{ L"R16_UNORM_X8_TYPELESS (Xbox)", DXGI_FORMAT(119) },
{ L"X16_TYPELESS_G8_UINT (Xbox)", DXGI_FORMAT(120) },
{ L"R10G10B10_SNORM_A2_UNORM (Xbox)", DXGI_FORMAT(189) },
{ L"R4G4_UNORM (Xbox)", DXGI_FORMAT(190) },
{ nullptr, DXGI_FORMAT_UNKNOWN }
};
#undef DEFFMT
const SValue<uint32_t> g_pFilters[] =
{
{ L"POINT", TEX_FILTER_POINT },
{ L"LINEAR", TEX_FILTER_LINEAR },
{ L"CUBIC", TEX_FILTER_CUBIC },
{ L"FANT", TEX_FILTER_FANT },
{ L"BOX", TEX_FILTER_BOX },
{ L"TRIANGLE", TEX_FILTER_TRIANGLE },
{ L"POINT_DITHER", TEX_FILTER_POINT | TEX_FILTER_DITHER },
{ L"LINEAR_DITHER", TEX_FILTER_LINEAR | TEX_FILTER_DITHER },
{ L"CUBIC_DITHER", TEX_FILTER_CUBIC | TEX_FILTER_DITHER },
{ L"FANT_DITHER", TEX_FILTER_FANT | TEX_FILTER_DITHER },
{ L"BOX_DITHER", TEX_FILTER_BOX | TEX_FILTER_DITHER },
{ L"TRIANGLE_DITHER", TEX_FILTER_TRIANGLE | TEX_FILTER_DITHER },
{ L"POINT_DITHER_DIFFUSION", TEX_FILTER_POINT | TEX_FILTER_DITHER_DIFFUSION },
{ L"LINEAR_DITHER_DIFFUSION", TEX_FILTER_LINEAR | TEX_FILTER_DITHER_DIFFUSION },
{ L"CUBIC_DITHER_DIFFUSION", TEX_FILTER_CUBIC | TEX_FILTER_DITHER_DIFFUSION },
{ L"FANT_DITHER_DIFFUSION", TEX_FILTER_FANT | TEX_FILTER_DITHER_DIFFUSION },
{ L"BOX_DITHER_DIFFUSION", TEX_FILTER_BOX | TEX_FILTER_DITHER_DIFFUSION },
{ L"TRIANGLE_DITHER_DIFFUSION", TEX_FILTER_TRIANGLE | TEX_FILTER_DITHER_DIFFUSION },
{ nullptr, TEX_FILTER_DEFAULT }
};
#define CODEC_DDS 0xFFFF0001
#define CODEC_TGA 0xFFFF0002
#define CODEC_HDR 0xFFFF0005
#ifdef USE_OPENEXR
#define CODEC_EXR 0xFFFF0006
#endif
#ifdef USE_LIBJPEG
#define CODEC_JPEG 0xFFFF0007
#endif
#ifdef USE_LIBPNG
#define CODEC_PNG 0xFFFF0008
#endif
const SValue<uint32_t> g_pDumpFileTypes[] =
{
{ L"bmp", WIC_CODEC_BMP },
#ifdef USE_LIBJPEG
{ L"jpg", CODEC_JPEG },
{ L"jpeg", CODEC_JPEG },
#else
{ L"jpg", WIC_CODEC_JPEG },
{ L"jpeg", WIC_CODEC_JPEG },
#endif
#ifdef USE_LIBPNG
{ L"png", CODEC_PNG },
#else
{ L"png", WIC_CODEC_PNG },
#endif
{ L"tga", CODEC_TGA },
{ L"hdr", CODEC_HDR },
{ L"tif", WIC_CODEC_TIFF },
{ L"tiff", WIC_CODEC_TIFF },
{ L"jxr", WIC_CODEC_WMP },
#ifdef USE_OPENEXR
{ L"exr", CODEC_EXR },
#endif
{ nullptr, CODEC_DDS }
};
const SValue<uint32_t> g_pExtFileTypes[] =
{
{ L".bmp", WIC_CODEC_BMP },
#ifdef USE_LIBJPEG
{ L".jpg", CODEC_JPEG },
{ L".jpeg", CODEC_JPEG },
#else
{ L".jpg", WIC_CODEC_JPEG },
{ L".jpeg", WIC_CODEC_JPEG },
#endif
#ifdef USE_LIBPNG
{ L".png", CODEC_PNG },
#else
{ L".png", WIC_CODEC_PNG },
#endif
{ L".dds", CODEC_DDS },
{ L".tga", CODEC_TGA },
{ L".hdr", CODEC_HDR },
{ L".tif", WIC_CODEC_TIFF },
{ L".tiff", WIC_CODEC_TIFF },
{ L".wdp", WIC_CODEC_WMP },
{ L".hdp", WIC_CODEC_WMP },
{ L".jxr", WIC_CODEC_WMP },
#ifdef USE_OPENEXR
{ L"exr", CODEC_EXR },
#endif
{ nullptr, CODEC_DDS }
};
void PrintUsage()
{
PrintLogo(false, g_ToolName, g_Description);
static const wchar_t* const s_usage =
L"Usage: texdiag <command> <options> [--] <files>\n"
L"\n"
L" info Output image metadata\n"
L" analyze Analyze and summarize image information\n"
L" compare Compare two images with MSE error metric\n"
L" diff Generate difference image from two images\n"
L" dumpbc Dump out compressed blocks (DDS BC only)\n"
L" dumpdds Dump out all the images in a complex DDS\n"
L"\n"
L" -r wildcard filename search is recursive\n"
L" -if <filter> image filtering\n"
L"\n"
L" (DDS input only)\n"
L" -t{u|f} TYPELESS format is treated as UNORM or FLOAT\n"
L" -dword Use DWORD instead of BYTE alignment\n"
L" -badtails Fix for older DXTn with bad mipchain tails\n"
L" -permissive Allow some DX9 variants with unusual header values\n"
L" -ignoremips Reads just the top-level mip which reads some invalid files\n"
L" -xlum expand legacy L8, L16, and A8P8 formats\n"
L"\n"
L" (diff only)\n"
L" -f <format> format\n"
L" -o <filename> output filename\n"
L" -l force output filename to lower case\n"
L" -y overwrite existing output file (if any)\n"
L" -c <hex-RGB> highlight difference color (defaults to off)\n"
L" -t <threshold> highlight threshold (defaults to 0.25)\n"
L"\n"
L" (dumpbc only)\n"
L" -targetx <num> dump pixels at location x (defaults to all)\n"
L" -targety <num> dump pixels at location y (defaults to all)\n"
L"\n"
L" (dumpdds only)\n"
L" -ft <filetype> output file type\n"
L"\n"
L" -nologo suppress copyright message\n"
L" -flist <filename> use text file with a list of input files (one per line)\n"
L"\n"
L" '-- ' is needed if any input filepath starts with the '-' or '/' character\n";
wprintf(L"%ls", s_usage);
wprintf(L"\n <format>: ");
PrintList(13, g_pFormats);
wprintf(L" ");
PrintList(13, g_pFormatAliases);
wprintf(L"\n <filter>: ");
PrintList(13, g_pFilters);
wprintf(L"\n <filetype>: ");
PrintList(15, g_pDumpFileTypes);
}
HRESULT LoadImage(
const wchar_t *fileName,
uint32_t dwOptions,
TEX_FILTER_FLAGS dwFilter,
TexMetadata& info,
std::unique_ptr<ScratchImage>& image)
{
if (!fileName)
return E_INVALIDARG;
image.reset(new (std::nothrow) ScratchImage);
if (!image)
return E_OUTOFMEMORY;
std::filesystem::path fname(fileName);
auto const ext = fname.extension();
if (_wcsicmp(ext.c_str(), L".dds") == 0)
{
DDS_FLAGS ddsFlags = DDS_FLAGS_ALLOW_LARGE_FILES;
if (dwOptions & (1 << OPT_DDS_DWORD_ALIGN))
ddsFlags |= DDS_FLAGS_LEGACY_DWORD;
if (dwOptions & (1 << OPT_EXPAND_LUMINANCE))
ddsFlags |= DDS_FLAGS_EXPAND_LUMINANCE;
if (dwOptions & (1 << OPT_DDS_BAD_DXTN_TAILS))
ddsFlags |= DDS_FLAGS_BAD_DXTN_TAILS;
if (dwOptions & (1 << OPT_DDS_PERMISSIVE))
ddsFlags |= DDS_FLAGS_PERMISSIVE;
if (dwOptions & (1 << OPT_DDS_IGNORE_MIPS))
ddsFlags |= DDS_FLAGS_IGNORE_MIPS;
HRESULT hr = LoadFromDDSFile(fileName, ddsFlags, &info, *image);
if (FAILED(hr))
return hr;
if (IsTypeless(info.format))
{
if (dwOptions & (1 << OPT_TYPELESS_UNORM))
{
info.format = MakeTypelessUNORM(info.format);
}
else if (dwOptions & (1 << OPT_TYPELESS_FLOAT))
{
info.format = MakeTypelessFLOAT(info.format);
}
if (IsTypeless(info.format))
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
image->OverrideFormat(info.format);
}
return S_OK;
}
else if (_wcsicmp(ext.c_str(), L".tga") == 0)
{
return LoadFromTGAFile(fileName, TGA_FLAGS_NONE, &info, *image);
}
else if (_wcsicmp(ext.c_str(), L".hdr") == 0)
{
return LoadFromHDRFile(fileName, &info, *image);
}
#ifdef USE_OPENEXR
else if (_wcsicmp(ext.c_str(), L".exr") == 0)
{
return LoadFromEXRFile(fileName, &info, *image);
}
#endif
#ifdef USE_LIBJPEG
else if (_wcsicmp(ext.c_str(), L".jpg") == 0 || _wcsicmp(ext.c_str(), L".jpeg") == 0)
{
return LoadFromJPEGFile(fileName, &info, *image);
}
#endif
#ifdef USE_LIBPNG
else if (_wcsicmp(ext.c_str(), L".png") == 0)
{
return LoadFromPNGFile(fileName, &info, *image);
}
#endif
else
{
// WIC shares the same filter values for mode and dither
static_assert(static_cast<int>(WIC_FLAGS_DITHER) == static_cast<int>(TEX_FILTER_DITHER), "WIC_FLAGS_* & TEX_FILTER_* should match");
static_assert(static_cast<int>(WIC_FLAGS_DITHER_DIFFUSION) == static_cast<int>(TEX_FILTER_DITHER_DIFFUSION), "WIC_FLAGS_* & TEX_FILTER_* should match");
static_assert(static_cast<int>(WIC_FLAGS_FILTER_POINT) == static_cast<int>(TEX_FILTER_POINT), "WIC_FLAGS_* & TEX_FILTER_* should match");
static_assert(static_cast<int>(WIC_FLAGS_FILTER_LINEAR) == static_cast<int>(TEX_FILTER_LINEAR), "WIC_FLAGS_* & TEX_FILTER_* should match");
static_assert(static_cast<int>(WIC_FLAGS_FILTER_CUBIC) == static_cast<int>(TEX_FILTER_CUBIC), "WIC_FLAGS_* & TEX_FILTER_* should match");
static_assert(static_cast<int>(WIC_FLAGS_FILTER_FANT) == static_cast<int>(TEX_FILTER_FANT), "WIC_FLAGS_* & TEX_FILTER_* should match");
HRESULT hr = LoadFromWICFile(fileName, dwFilter | WIC_FLAGS_ALL_FRAMES, &info, *image);
if (hr == static_cast<HRESULT>(0xc00d5212) /* MF_E_TOPO_CODEC_NOT_FOUND */)
{
if (_wcsicmp(ext.c_str(), L".heic") == 0 || _wcsicmp(ext.c_str(), L".heif") == 0)
{
wprintf(L"\nINFO: This format requires installing the HEIF Image Extensions - https://aka.ms/heif\n");
}
else if (_wcsicmp(ext.c_str(), L".webp") == 0)
{
wprintf(L"\nINFO: This format requires installing the WEBP Image Extensions - https://www.microsoft.com/p/webp-image-extensions/9pg2dk419drg\n");
}
}
return hr;
}
}
HRESULT SaveImage(const Image* image, const wchar_t *fileName, uint32_t codec)
{
switch (codec)
{
case CODEC_DDS:
return SaveToDDSFile(*image, DDS_FLAGS_NONE, fileName);
case CODEC_TGA:
return SaveToTGAFile(*image, TGA_FLAGS_NONE, fileName);
case CODEC_HDR:
return SaveToHDRFile(*image, fileName);
#ifdef USE_OPENEXR
case CODEC_EXR:
return SaveToEXRFile(*image, fileName);
#endif
#ifdef USE_LIBJPEG
case CODEC_JPEG:
return SaveToJPEGFile(*image, fileName);
#endif
#ifdef USE_LIBPNG
case CODEC_PNG:
return SaveToPNGFile(*image, fileName);
#endif
default:
return SaveToWICFile(*image, WIC_FLAGS_NONE, GetWICCodec(static_cast<WICCodecs>(codec)), fileName);
}
}
//--------------------------------------------------------------------------------------
struct AnalyzeData
{
XMFLOAT4 imageMin;
XMFLOAT4 imageMax;
XMFLOAT4 imageAvg;
XMFLOAT4 imageVariance;
XMFLOAT4 imageStdDev;
float luminance;
size_t specials_x;
size_t specials_y;
size_t specials_z;
size_t specials_w;
void Print()
{
wprintf(L"\t Minimum - (%f %f %f %f)\n", imageMin.x, imageMin.y, imageMin.z, imageMin.w);
wprintf(L"\t Average - (%f %f %f %f)\n", imageAvg.x, imageAvg.y, imageAvg.z, imageAvg.w);
wprintf(L"\t Maximum - (%f %f %f %f)\n", imageMax.x, imageMax.y, imageMax.z, imageMax.w);
wprintf(L"\t Variance - (%f %f %f %f)\n", imageVariance.x, imageVariance.y, imageVariance.z, imageVariance.w);
wprintf(L"\t Std Dev - (%f %f %f %f)\n", imageStdDev.x, imageStdDev.y, imageStdDev.z, imageStdDev.w);
wprintf(L"\tLuminance - %f (maximum)\n", luminance);
if ((specials_x > 0) || (specials_y > 0) || (specials_z > 0) || (specials_w > 0))
{
wprintf(L" FP specials - (%zu %zu %zu %zu)\n", specials_x, specials_y, specials_z, specials_w);
}
}
};
HRESULT Analyze(const Image& image, _Out_ AnalyzeData& result)
{
memset(&result, 0, sizeof(AnalyzeData));
// First pass
XMVECTOR minv = g_XMFltMax;
XMVECTOR maxv = XMVectorNegate(g_XMFltMax);
XMVECTOR acc = g_XMZero;
XMVECTOR luminance = g_XMZero;
size_t totalPixels = 0;
HRESULT hr = EvaluateImage(image, [&](const XMVECTOR * pixels, size_t width, size_t y)
{
static const XMVECTORF32 s_luminance = { { { 0.3f, 0.59f, 0.11f, 0.f } } };
UNREFERENCED_PARAMETER(y);
for (size_t x = 0; x < width; ++x)
{
const XMVECTOR v = *pixels++;
luminance = XMVectorMax(luminance, XMVector3Dot(v, s_luminance));
minv = XMVectorMin(minv, v);
maxv = XMVectorMax(maxv, v);
acc = XMVectorAdd(v, acc);
++totalPixels;
XMFLOAT4 f;
XMStoreFloat4(&f, v);
if (!isfinite(f.x))
{
++result.specials_x;
}
if (!isfinite(f.y))
{
++result.specials_y;
}
if (!isfinite(f.z))
{
++result.specials_z;
}
if (!isfinite(f.w))
{
++result.specials_w;
}
}
});
if (FAILED(hr))
return hr;
if (!totalPixels)
return S_FALSE;
result.luminance = XMVectorGetX(luminance);
XMStoreFloat4(&result.imageMin, minv);
XMStoreFloat4(&result.imageMax, maxv);
const XMVECTOR pixelv = XMVectorReplicate(float(totalPixels));
XMVECTOR avgv = XMVectorDivide(acc, pixelv);
XMStoreFloat4(&result.imageAvg, avgv);
// Second pass
acc = g_XMZero;
hr = EvaluateImage(image, [&](const XMVECTOR * pixels, size_t width, size_t y)
{
UNREFERENCED_PARAMETER(y);
for (size_t x = 0; x < width; ++x)
{
const XMVECTOR v = *pixels++;
const XMVECTOR diff = XMVectorSubtract(v, avgv);
acc = XMVectorMultiplyAdd(diff, diff, acc);
}
});
if (FAILED(hr))
return hr;
XMStoreFloat4(&result.imageVariance, acc);
const XMVECTOR stddev = XMVectorSqrt(acc);
XMStoreFloat4(&result.imageStdDev, stddev);
return S_OK;
}
//--------------------------------------------------------------------------------------
struct AnalyzeBCData
{
size_t blocks;
size_t blockHist[15];
void Print(DXGI_FORMAT fmt)
{
wprintf(L"\t Compression - ");
PrintFormat(fmt, g_pFormats, g_pReadOnlyFormats);
wprintf(L"\n\t Total blocks - %zu\n", blocks);
switch (fmt)
{
case DXGI_FORMAT_BC1_UNORM:
case DXGI_FORMAT_BC1_UNORM_SRGB:
wprintf(L"\t 4 color blocks - %zu\n", blockHist[0]);
wprintf(L"\t 3 color blocks - %zu\n", blockHist[1]);
break;
// BC2 only has a single 'type' of block
case DXGI_FORMAT_BC3_UNORM:
case DXGI_FORMAT_BC3_UNORM_SRGB:
wprintf(L"\t 8 alpha blocks - %zu\n", blockHist[0]);
wprintf(L"\t 6 alpha blocks - %zu\n", blockHist[1]);
break;
case DXGI_FORMAT_BC4_UNORM:
case DXGI_FORMAT_BC4_SNORM:
wprintf(L"\t 8 red blocks - %zu\n", blockHist[0]);
wprintf(L"\t 6 red blocks - %zu\n", blockHist[1]);
break;
case DXGI_FORMAT_BC5_UNORM:
case DXGI_FORMAT_BC5_SNORM:
wprintf(L"\t 8 red blocks - %zu\n", blockHist[0]);
wprintf(L"\t 6 red blocks - %zu\n", blockHist[1]);
wprintf(L"\t 8 green blocks - %zu\n", blockHist[2]);
wprintf(L"\t 6 green blocks - %zu\n", blockHist[3]);
break;
case DXGI_FORMAT_BC6H_UF16:
case DXGI_FORMAT_BC6H_SF16:
for (size_t j = 1; j <= 14; ++j)
{
if (blockHist[j] > 0)
wprintf(L"\t Mode %02zu blocks - %zu\n", j, blockHist[j]);
}
if (blockHist[0] > 0)
wprintf(L"\tReserved mode blcks - %zu\n", blockHist[0]);
break;
case DXGI_FORMAT_BC7_UNORM:
case DXGI_FORMAT_BC7_UNORM_SRGB:
for (size_t j = 0; j <= 7; ++j)
{
if (blockHist[j] > 0)
wprintf(L"\t Mode %02zu blocks - %zu\n", j, blockHist[j]);
}
if (blockHist[8] > 0)
wprintf(L"\tReserved mode blcks - %zu\n", blockHist[8]);
break;
default:
break;
}
}
};
#pragma pack(push,1)
struct BC1Block
{
uint16_t rgb[2]; // 565 colors
uint32_t bitmap; // 2bpp rgb bitmap
};
struct BC2Block
{
uint32_t bitmap[2]; // 4bpp alpha bitmap
BC1Block bc1; // BC1 rgb data
};
struct BC3Block
{
uint8_t alpha[2]; // alpha values
uint8_t bitmap[6]; // 3bpp alpha bitmap
BC1Block bc1; // BC1 rgb data
};
struct BC4UBlock
{
uint8_t red_0;
uint8_t red_1;
uint8_t indices[6];
};
struct BC4SBlock
{
int8_t red_0;
int8_t red_1;
uint8_t indices[6];
};
struct BC5UBlock
{
BC4UBlock u;
BC4UBlock v;
};
struct BC5SBlock
{
BC4SBlock u;
BC4SBlock v;
};
#pragma pack(pop)
HRESULT AnalyzeBC(const Image& image, _Out_ AnalyzeBCData& result)
{
memset(&result, 0, sizeof(AnalyzeBCData));
size_t sbpp;
switch (image.format)
{
case DXGI_FORMAT_BC1_UNORM:
case DXGI_FORMAT_BC1_UNORM_SRGB:
case DXGI_FORMAT_BC4_UNORM:
case DXGI_FORMAT_BC4_SNORM:
sbpp = 8;
break;
case DXGI_FORMAT_BC2_UNORM:
case DXGI_FORMAT_BC2_UNORM_SRGB:
case DXGI_FORMAT_BC3_UNORM:
case DXGI_FORMAT_BC3_UNORM_SRGB:
case DXGI_FORMAT_BC5_UNORM:
case DXGI_FORMAT_BC5_SNORM:
case DXGI_FORMAT_BC6H_UF16:
case DXGI_FORMAT_BC6H_SF16:
case DXGI_FORMAT_BC7_UNORM:
case DXGI_FORMAT_BC7_UNORM_SRGB:
sbpp = 16;
break;
default:
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
const uint8_t *pSrc = image.pixels;
const size_t rowPitch = image.rowPitch;
for (size_t h = 0; h < image.height; h += 4)
{
const uint8_t *sptr = pSrc;
for (size_t count = 0; count < rowPitch; count += sbpp)
{
switch (image.format)
{
case DXGI_FORMAT_BC1_UNORM:
case DXGI_FORMAT_BC1_UNORM_SRGB:
{
auto block = reinterpret_cast<const BC1Block*>(sptr);
if (block->rgb[0] <= block->rgb[1])
{
// Transparent block
++result.blockHist[1];
}
else
{
// Opaque block
++result.blockHist[0];
}
}
break;
// BC2 only has a single 'type' of block
case DXGI_FORMAT_BC3_UNORM:
case DXGI_FORMAT_BC3_UNORM_SRGB:
{
auto block = reinterpret_cast<const BC3Block*>(sptr);
if (block->alpha[0] > block->alpha[1])
{
// 8 alpha block
++result.blockHist[0];
}
else
{
// 6 alpha block
++result.blockHist[1];
}
}
break;
case DXGI_FORMAT_BC4_UNORM:
{
auto block = reinterpret_cast<const BC4UBlock*>(sptr);
if (block->red_0 > block->red_1)
{
// 8 red block
++result.blockHist[0];
}
else
{
// 6 red block
++result.blockHist[1];
}
}
break;
case DXGI_FORMAT_BC4_SNORM:
{
auto block = reinterpret_cast<const BC4SBlock*>(sptr);
if (block->red_0 > block->red_1)
{
// 8 red block
++result.blockHist[0];
}
else
{
// 6 red block
++result.blockHist[1];
}
}
break;
case DXGI_FORMAT_BC5_UNORM:
{
auto block = reinterpret_cast<const BC5UBlock*>(sptr);
if (block->u.red_0 > block->u.red_1)
{
// 8 red block
++result.blockHist[0];
}
else
{
// 6 red block
++result.blockHist[1];
}
if (block->v.red_0 > block->v.red_1)
{
// 8 green block
++result.blockHist[2];
}
else
{
// 6 green block
++result.blockHist[3];
}
}
break;
case DXGI_FORMAT_BC5_SNORM:
{
auto block = reinterpret_cast<const BC5SBlock*>(sptr);
if (block->u.red_0 > block->u.red_1)
{
// 8 red block
++result.blockHist[0];
}
else
{
// 6 red block
++result.blockHist[1];
}
if (block->v.red_0 > block->v.red_1)
{
// 8 green block
++result.blockHist[2];
}
else
{
// 6 green block
++result.blockHist[3];
}
}
break;
case DXGI_FORMAT_BC6H_UF16:
case DXGI_FORMAT_BC6H_SF16:
switch (*sptr & 0x03)
{
case 0x00:
// Mode 1 (2 bits, 00)
++result.blockHist[1];
break;
case 0x01:
// Mode 2 (2 bits, 01)
++result.blockHist[2];
break;
default:
switch (*sptr & 0x1F)
{
case 0x02:
// Mode 3 (5 bits, 00010)
++result.blockHist[3];
break;
case 0x06:
// Mode 4 (5 bits, 00110)
++result.blockHist[4];
break;
case 0x0A:
// Mode 5 (5 bits, 01010)
++result.blockHist[5];
break;
case 0x0E:
// Mode 6 (5 bits, 01110)
++result.blockHist[6];
break;
case 0x12:
// Mode 7 (5 bits, 10010)
++result.blockHist[7];
break;
case 0x16:
// Mode 8 (5 bits, 10110)
++result.blockHist[8];
break;
case 0x1A:
// Mode 9 (5 bits, 11010)
++result.blockHist[9];
break;
case 0x1E:
// Mode 10 (5 bits, 11110)
++result.blockHist[10];
break;
case 0x03:
// Mode 11 (5 bits, 00011)
++result.blockHist[11];
break;
case 0x07:
// Mode 12 (5 bits, 00111)
++result.blockHist[12];
break;
case 0x0B:
// Mode 13 (5 bits, 01011)
++result.blockHist[13];
break;
case 0x0F:
// Mode 14 (5 bits, 01111)
++result.blockHist[14];
break;
case 0x13: // Reserved mode (5 bits, 10011)
case 0x17: // Reserved mode (5 bits, 10111)
case 0x1B: // Reserved mode (5 bits, 11011)
case 0x1F: // Reserved mode (5 bits, 11111)
default:
++result.blockHist[0];
break;
}
break;
}
break;
case DXGI_FORMAT_BC7_UNORM:
case DXGI_FORMAT_BC7_UNORM_SRGB:
if (*sptr & 0x01)
{
// Mode 0 (1)
++result.blockHist[0];
}
else if (*sptr & 0x02)
{
// Mode 1 (01)
++result.blockHist[1];
}
else if (*sptr & 0x04)
{
// Mode 2 (001)
++result.blockHist[2];
}
else if (*sptr & 0x08)
{
// Mode 3 (0001)
++result.blockHist[3];
}
else if (*sptr & 0x10)
{
// Mode 4 (00001)
++result.blockHist[4];
}
else if (*sptr & 0x20)
{
// Mode 5 (000001)
++result.blockHist[5];
}
else if (*sptr & 0x40)
{
// Mode 6 (0000001)
++result.blockHist[6];
}
else if (*sptr & 0x80)
{
// Mode 7 (00000001)
++result.blockHist[7];
}
else
{
// Reserved mode 8 (00000000)
++result.blockHist[8];
}
break;
default:
break;
}
sptr += sbpp;
++result.blocks;
}
pSrc += rowPitch;
}
return S_OK;
}
//--------------------------------------------------------------------------------------
HRESULT Difference(
const Image& image1,
const Image& image2,
TEX_FILTER_FLAGS dwFilter,
DXGI_FORMAT format,
uint32_t diffColor,
float threshold,
ScratchImage& result)
{
if (!image1.pixels || !image2.pixels)
return E_POINTER;
if (image1.width != image2.width
|| image1.height != image2.height)
return E_FAIL;
ScratchImage tempA;
const Image* imageA = &image1;
if (IsCompressed(image1.format))
{
HRESULT hr = Decompress(image1, DXGI_FORMAT_R32G32B32A32_FLOAT, tempA);
if (FAILED(hr))
return hr;
imageA = tempA.GetImage(0, 0, 0);
}
ScratchImage tempB;
const Image* imageB = &image2;
if (image2.format != DXGI_FORMAT_R32G32B32A32_FLOAT)
{
if (IsCompressed(image2.format))
{
HRESULT hr = Decompress(image2, DXGI_FORMAT_R32G32B32A32_FLOAT, tempB);
if (FAILED(hr))
return hr;
imageB = tempB.GetImage(0, 0, 0);
}
else
{
HRESULT hr = Convert(image2, DXGI_FORMAT_R32G32B32A32_FLOAT, dwFilter, TEX_THRESHOLD_DEFAULT, tempB);
if (FAILED(hr))
return hr;
imageB = tempB.GetImage(0, 0, 0);
}
}
if (!imageA || !imageB)
return E_POINTER;
XMVECTOR colorValue = PackedVector::XMLoadColor(reinterpret_cast<const PackedVector::XMCOLOR*>(&diffColor));
colorValue = XMVectorSelect(g_XMIdentityR3, colorValue, g_XMSelect1110);
ScratchImage diffImage;
HRESULT hr = TransformImage(*imageA, [&](XMVECTOR* outPixels, const XMVECTOR * inPixels, size_t width, size_t y)
{
const XMVECTOR tolerance = XMVectorReplicate(threshold);
auto *inPixelsB = reinterpret_cast<XMVECTOR*>(imageB->pixels + (y*imageB->rowPitch));
for (size_t x = 0; x < width; ++x)
{
XMVECTOR v1 = *inPixels++;
const XMVECTOR v2 = *inPixelsB++;
v1 = XMVectorSubtract(v1, v2);
v1 = XMVectorAbs(v1);
v1 = XMVectorSelect(g_XMIdentityR3, v1, g_XMSelect1110);
if (diffColor && XMVector3GreaterOrEqual(v1, tolerance))
{
*outPixels++ = colorValue;
}
else
{
*outPixels++ = v1;
}
}
}, (format == DXGI_FORMAT_R32G32B32A32_FLOAT) ? result : diffImage);
if (FAILED(hr))
return hr;
if (format == diffImage.GetMetadata().format)
{
std::swap(diffImage, result);
return S_OK;
}
return Convert(diffImage.GetImages(), diffImage.GetImageCount(), diffImage.GetMetadata(), format, dwFilter, TEX_THRESHOLD_DEFAULT, result);
}
#pragma region DumpBC
//--------------------------------------------------------------------------------------
// Partition, Shape, Fixup
const uint8_t g_aFixUp[3][64][3] =
{
{ // No fix-ups for 1st subset for BC6H or BC7
{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },
{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },
{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },
{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },
{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },
{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },
{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },
{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },
{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },
{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },
{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },
{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },
{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },
{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },
{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },
{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 }
},
{ // BC6H/BC7 Partition Set Fixups for 2 Subsets
{ 0,15, 0 },{ 0,15, 0 },{ 0,15, 0 },{ 0,15, 0 },
{ 0,15, 0 },{ 0,15, 0 },{ 0,15, 0 },{ 0,15, 0 },
{ 0,15, 0 },{ 0,15, 0 },{ 0,15, 0 },{ 0,15, 0 },
{ 0,15, 0 },{ 0,15, 0 },{ 0,15, 0 },{ 0,15, 0 },
{ 0,15, 0 },{ 0, 2, 0 },{ 0, 8, 0 },{ 0, 2, 0 },
{ 0, 2, 0 },{ 0, 8, 0 },{ 0, 8, 0 },{ 0,15, 0 },
{ 0, 2, 0 },{ 0, 8, 0 },{ 0, 2, 0 },{ 0, 2, 0 },
{ 0, 8, 0 },{ 0, 8, 0 },{ 0, 2, 0 },{ 0, 2, 0 },
// BC7 Partition Set Fixups for 2 Subsets (second-half)
{ 0,15, 0 },{ 0,15, 0 },{ 0, 6, 0 },{ 0, 8, 0 },
{ 0, 2, 0 },{ 0, 8, 0 },{ 0,15, 0 },{ 0,15, 0 },
{ 0, 2, 0 },{ 0, 8, 0 },{ 0, 2, 0 },{ 0, 2, 0 },
{ 0, 2, 0 },{ 0,15, 0 },{ 0,15, 0 },{ 0, 6, 0 },
{ 0, 6, 0 },{ 0, 2, 0 },{ 0, 6, 0 },{ 0, 8, 0 },
{ 0,15, 0 },{ 0,15, 0 },{ 0, 2, 0 },{ 0, 2, 0 },
{ 0,15, 0 },{ 0,15, 0 },{ 0,15, 0 },{ 0,15, 0 },
{ 0,15, 0 },{ 0, 2, 0 },{ 0, 2, 0 },{ 0,15, 0 }
},
{ // BC7 Partition Set Fixups for 3 Subsets
{ 0, 3,15 },{ 0, 3, 8 },{ 0,15, 8 },{ 0,15, 3 },
{ 0, 8,15 },{ 0, 3,15 },{ 0,15, 3 },{ 0,15, 8 },
{ 0, 8,15 },{ 0, 8,15 },{ 0, 6,15 },{ 0, 6,15 },
{ 0, 6,15 },{ 0, 5,15 },{ 0, 3,15 },{ 0, 3, 8 },
{ 0, 3,15 },{ 0, 3, 8 },{ 0, 8,15 },{ 0,15, 3 },
{ 0, 3,15 },{ 0, 3, 8 },{ 0, 6,15 },{ 0,10, 8 },
{ 0, 5, 3 },{ 0, 8,15 },{ 0, 8, 6 },{ 0, 6,10 },
{ 0, 8,15 },{ 0, 5,15 },{ 0,15,10 },{ 0,15, 8 },
{ 0, 8,15 },{ 0,15, 3 },{ 0, 3,15 },{ 0, 5,10 },
{ 0, 6,10 },{ 0,10, 8 },{ 0, 8, 9 },{ 0,15,10 },
{ 0,15, 6 },{ 0, 3,15 },{ 0,15, 8 },{ 0, 5,15 },
{ 0,15, 3 },{ 0,15, 6 },{ 0,15, 6 },{ 0,15, 8 },
{ 0, 3,15 },{ 0,15, 3 },{ 0, 5,15 },{ 0, 5,15 },
{ 0, 5,15 },{ 0, 8,15 },{ 0, 5,15 },{ 0,10,15 },
{ 0, 5,15 },{ 0,10,15 },{ 0, 8,15 },{ 0,13,15 },
{ 0,15, 3 },{ 0,12,15 },{ 0, 3,15 },{ 0, 3, 8 }
}
};
inline static bool IsFixUpOffset(
_In_range_(0, 2) size_t uPartitions,
_In_range_(0, 63) uint64_t uShape,
_In_range_(0, 15) size_t uOffset)
{
for (size_t p = 0; p <= uPartitions; p++)
{
if (uOffset == g_aFixUp[uPartitions][uShape][p])
{
return true;
}
}
return false;
}
//--------------------------------------------------------------------------------------
#define SIGN_EXTEND(x,nb) ((((x)&(1<<((nb)-1)))?((~0)^((1<<(nb))-1)):0)|(x))
#define NUM_PIXELS_PER_BLOCK 16
void Print565(uint16_t rgb)
{
auto const r = float(((rgb >> 11) & 31) * (1.0f / 31.0f));
auto const g = float(((rgb >> 5) & 63) * (1.0f / 63.0f));
auto const b = float(((rgb >> 0) & 31) * (1.0f / 31.0f));
wprintf(L"(R: %.3f, G: %.3f, B: %.3f)", r, g, b);
}
void PrintIndex2bpp(uint32_t bitmap)
{
for (size_t j = 0; j < NUM_PIXELS_PER_BLOCK; ++j, bitmap >>= 2)
{
wprintf(L"%u%ls", bitmap & 0x3, ((j < (NUM_PIXELS_PER_BLOCK - 1)) && ((j % 4) == 3)) ? L" | " : L" ");
}
}
void PrintIndex2bpp(uint64_t bitmap, size_t parts, uint64_t shape)
{
for (size_t j = 0; j < NUM_PIXELS_PER_BLOCK; ++j)
{
if (IsFixUpOffset(parts, shape, j))
{
wprintf(L"%llu%ls", bitmap & 0x1, ((j < (NUM_PIXELS_PER_BLOCK - 1)) && ((j % 4) == 3)) ? L" | " : L" ");
bitmap >>= 1;
}
else
{
wprintf(L"%llu%ls", bitmap & 0x3, ((j < (NUM_PIXELS_PER_BLOCK - 1)) && ((j % 4) == 3)) ? L" | " : L" ");
bitmap >>= 2;
}
}
}
void PrintIndex3bpp(uint64_t bitmap, size_t parts, uint64_t shape)
{
for (size_t j = 0; j < NUM_PIXELS_PER_BLOCK; ++j)
{
if (IsFixUpOffset(parts, shape, j))
{
wprintf(L"%llu%ls", bitmap & 0x3, ((j < (NUM_PIXELS_PER_BLOCK - 1)) && ((j % 4) == 3)) ? L" | " : L" ");
bitmap >>= 2;
}
else
{
wprintf(L"%llu%ls", bitmap & 0x7, ((j < (NUM_PIXELS_PER_BLOCK - 1)) && ((j % 4) == 3)) ? L" | " : L" ");
bitmap >>= 3;
}
}
}
void PrintIndex4bpp(uint64_t bitmap, size_t parts, uint64_t shape)
{
for (size_t j = 0; j < NUM_PIXELS_PER_BLOCK; ++j)
{
if (IsFixUpOffset(parts, shape, j))
{
wprintf(L"%llX%ls", bitmap & 0x7, ((j < (NUM_PIXELS_PER_BLOCK - 1)) && ((j % 4) == 3)) ? L" | " : L" ");
bitmap >>= 3;
}
else
{
wprintf(L"%llX%ls", bitmap & 0xF, ((j < (NUM_PIXELS_PER_BLOCK - 1)) && ((j % 4) == 3)) ? L" | " : L" ");
bitmap >>= 4;
}
}
}
void PrintIndex3bpp(const uint8_t data[6])
{
uint32_t bitmap = uint32_t(data[0]) | (uint32_t(data[1]) << 8) | (uint32_t(data[2]) << 16);
size_t j = 0;
for (; j < (NUM_PIXELS_PER_BLOCK / 2); ++j, bitmap >>= 3)
{
wprintf(L"%u%ls", bitmap & 0x7, ((j % 4) == 3) ? L" | " : L" ");
}
bitmap = uint32_t(data[3]) | (uint32_t(data[4]) << 8) | (uint32_t(data[5]) << 16);
for (; j < NUM_PIXELS_PER_BLOCK; ++j, bitmap >>= 3)
{
wprintf(L"%u%ls", bitmap & 0x7, ((j < (NUM_PIXELS_PER_BLOCK - 1)) && ((j % 4) == 3)) ? L" | " : L" ");
}
}
const wchar_t* GetRotBits(uint64_t rot)
{
switch (rot)
{
case 1: return L" (R<->A)";
case 2: return L" (G<->A)";
case 3: return L" (B<->A)";
default: return L"";
}
}
HRESULT DumpBCImage(const Image& image, int pixelx, int pixely)
{
size_t sbpp;
switch (image.format)
{
case DXGI_FORMAT_BC1_UNORM:
case DXGI_FORMAT_BC1_UNORM_SRGB:
case DXGI_FORMAT_BC4_UNORM:
case DXGI_FORMAT_BC4_SNORM:
sbpp = 8;
break;
case DXGI_FORMAT_BC2_UNORM:
case DXGI_FORMAT_BC2_UNORM_SRGB:
case DXGI_FORMAT_BC3_UNORM:
case DXGI_FORMAT_BC3_UNORM_SRGB:
case DXGI_FORMAT_BC5_UNORM:
case DXGI_FORMAT_BC5_SNORM:
case DXGI_FORMAT_BC6H_UF16:
case DXGI_FORMAT_BC6H_SF16:
case DXGI_FORMAT_BC7_UNORM:
case DXGI_FORMAT_BC7_UNORM_SRGB:
sbpp = 16;
break;
default:
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
const uint8_t *pSrc = image.pixels;
const size_t rowPitch = image.rowPitch;
size_t nblock = 0;
for (size_t h = 0; h < image.height; h += 4, pSrc += rowPitch)
{
if (pixely >= 0)
{
if ((pixely < int(h)) || (pixely >= int(h + 4)))
continue;
}
const uint8_t *sptr = pSrc;
size_t w = 0;
for (size_t count = 0; count < rowPitch; count += sbpp, w += 4, ++nblock, sptr += sbpp)
{
if (pixelx >= 0)
{
if ((pixelx < int(w)) || (pixelx >= int(w + 4)))
continue;
}
wprintf(L" Block %zu (pixel: %zu x %zu)\n", nblock, w, h);
switch (image.format)
{
case DXGI_FORMAT_BC1_UNORM:
case DXGI_FORMAT_BC1_UNORM_SRGB:
{
auto block = reinterpret_cast<const BC1Block*>(sptr);
if (block->rgb[0] <= block->rgb[1])
{
// Transparent block
wprintf(L"\tTransparent - E0: ");
}
else
{
// Opaque block
wprintf(L"\t Opaque - E0: ");
}
Print565(block->rgb[0]);
wprintf(L"\n\t E1: ");
Print565(block->rgb[1]);
wprintf(L"\n\t Index: ");
PrintIndex2bpp(block->bitmap);
wprintf(L"\n");
}
break;
case DXGI_FORMAT_BC2_UNORM:
case DXGI_FORMAT_BC2_UNORM_SRGB:
{
auto block = reinterpret_cast<const BC2Block*>(sptr);
wprintf(L"\tColor - E0: ");
Print565(block->bc1.rgb[0]);
wprintf(L"\n\t E1: ");
Print565(block->bc1.rgb[1]);
wprintf(L"\n\t Index: ");
PrintIndex2bpp(block->bc1.bitmap);
wprintf(L"\n");
wprintf(L"\tAlpha - ");
size_t j = 0;
uint32_t bitmap = block->bitmap[0];
for (; j < (NUM_PIXELS_PER_BLOCK / 2); ++j, bitmap >>= 4)
{
wprintf(L"%X%ls", bitmap & 0xF, ((j % 4) == 3) ? L" | " : L" ");
}
bitmap = block->bitmap[1];
for (; j < NUM_PIXELS_PER_BLOCK; ++j, bitmap >>= 4)
{
wprintf(L"%X%ls", bitmap & 0xF, ((j < (NUM_PIXELS_PER_BLOCK - 1)) && ((j % 4) == 3)) ? L" | " : L" ");
}
wprintf(L"\n");
}
break;
case DXGI_FORMAT_BC3_UNORM:
case DXGI_FORMAT_BC3_UNORM_SRGB:
{
auto block = reinterpret_cast<const BC3Block*>(sptr);
wprintf(L"\tColor - E0: ");
Print565(block->bc1.rgb[0]);
wprintf(L"\n\t E1: ");
Print565(block->bc1.rgb[1]);
wprintf(L"\n\t Index: ");
PrintIndex2bpp(block->bc1.bitmap);
wprintf(L"\n");
wprintf(L"\tAlpha - E0: %0.3f E1: %0.3f (%u)\n\t Index: ",
(float(block->alpha[0]) / 255.f),
(float(block->alpha[1]) / 255.f), (block->alpha[0] > block->alpha[1]) ? 8u : 6u);
PrintIndex3bpp(block->bitmap);
wprintf(L"\n");
}
break;
case DXGI_FORMAT_BC4_UNORM:
{
auto block = reinterpret_cast<const BC4UBlock*>(sptr);
wprintf(L"\t E0: %0.3f E1: %0.3f (%u)\n\tIndex: ",
(float(block->red_0) / 255.f),
(float(block->red_1) / 255.f), (block->red_0 > block->red_1) ? 8u : 6u);
PrintIndex3bpp(block->indices);
wprintf(L"\n");
}
break;
case DXGI_FORMAT_BC4_SNORM:
{
auto block = reinterpret_cast<const BC4SBlock*>(sptr);
wprintf(L"\t E0: %0.3f E1: %0.3f (%u)\n\tIndex: ",
(float(block->red_0) / 127.f),
(float(block->red_1) / 127.f), (block->red_0 > block->red_1) ? 8u : 6u);
PrintIndex3bpp(block->indices);
wprintf(L"\n");
}
break;
case DXGI_FORMAT_BC5_UNORM:
{
auto block = reinterpret_cast<const BC5UBlock*>(sptr);
wprintf(L"\tU - E0: %0.3f E1: %0.3f (%u)\n\t Index: ",
(float(block->u.red_0) / 255.f),
(float(block->u.red_1) / 255.f), (block->u.red_0 > block->u.red_1) ? 8u : 6u);
PrintIndex3bpp(block->u.indices);
wprintf(L"\n");
wprintf(L"\tV - E0: %0.3f E1: %0.3f (%u)\n\t Index: ",
(float(block->v.red_0) / 255.f),
(float(block->v.red_1) / 255.f), (block->v.red_0 > block->v.red_1) ? 8u : 6u);
PrintIndex3bpp(block->v.indices);
wprintf(L"\n");
}
break;
case DXGI_FORMAT_BC5_SNORM:
{
auto block = reinterpret_cast<const BC5SBlock*>(sptr);
wprintf(L"\tU - E0: %0.3f E1: %0.3f (%u)\n\t Index: ",
(float(block->u.red_0) / 127.f),
(float(block->u.red_1) / 127.f), (block->u.red_0 > block->u.red_1) ? 8u : 6u);
PrintIndex3bpp(block->u.indices);
wprintf(L"\n");
wprintf(L"\tV - E0: %0.3f E1: %0.3f (%u)\n\t Index: ",
(float(block->v.red_0) / 127.f),
(float(block->v.red_1) / 127.f), (block->v.red_0 > block->v.red_1) ? 8u : 6u);
PrintIndex3bpp(block->v.indices);
wprintf(L"\n");
}
break;
case DXGI_FORMAT_BC6H_UF16:
case DXGI_FORMAT_BC6H_SF16:
// http://msdn.microsoft.com/en-us/library/windows/desktop/hh308952.aspx#decoding_the_bc6h_format
switch (*sptr & 0x03)
{
case 0x00:
// Mode 1 (2 bits, 00)
{
struct bc6h_mode1
{
uint64_t mode : 2; // { M, 0}, { M, 1}
uint64_t gy4 : 1; // {GY, 4}
uint64_t by4 : 1; // {BY, 4}
uint64_t bz4 : 1; // {BZ, 4}
uint64_t rw : 10; // {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, {RW, 5}, {RW, 6}, {RW, 7}, {RW, 8}, {RW, 9}
uint64_t gw : 10; // {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, {GW, 5}, {GW, 6}, {GW, 7}, {GW, 8}, {GW, 9}
uint64_t bw : 10; // {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, {BW, 5}, {BW, 6}, {BW, 7}, {BW, 8}, {BW, 9}
uint64_t rx : 5; // {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}, {RX, 4}
uint64_t gz4 : 1; // {GZ, 4}
uint64_t gy : 4; // {GY, 0}, {GY, 1}, {GY, 2}, {GY, 3}
uint64_t gx : 5; // {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}, {GX, 4}
uint64_t bz0 : 1; // {BZ, 0},
uint64_t gz : 4; // {GZ, 0}, {GZ, 1}, {GZ, 2}, {GZ, 3}
uint64_t bx : 5; // {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}, {BX, 4}
uint64_t bz1 : 1; // {BZ, 1}
uint64_t by : 3; // {BY, 0}, {BY, 1}, {BY, 2}
uint64_t by3 : 1; // {BY, 3}
uint64_t ry : 5; // {RY, 0}, {RY, 1}, {RY, 2}, {RY, 3}, {RY, 4}
uint64_t bz2 : 1; // {BZ, 2}
uint64_t rz : 5; // {RZ, 0}, {RZ, 1}, {RZ, 2}, {RZ, 3}, {RZ, 4}
uint64_t bz3 : 1; // {BZ, 3}
uint64_t d : 5; // { D, 0}, { D, 1}, { D, 2}, { D, 3}, { D, 4}
uint64_t indices : 46;
};
static_assert(sizeof(bc6h_mode1) == 16, "Block size must be 16 bytes");
const bool bSigned = (image.format == DXGI_FORMAT_BC6H_SF16) ? true : false;
auto m = reinterpret_cast<const bc6h_mode1*>(sptr);
XMINT3 e0_A(int(m->rw), int(m->gw), int(m->bw));
XMINT3 e0_B(int(m->rx), int(m->gx), int(m->bx));
XMINT3 e1_A(int(m->ry),
int(m->gy | (m->gy4 << 4)),
int(m->by | (m->by3 << 3) | (m->by4 << 4)));
XMINT3 e1_B(int(m->rz),
int(m->gz | (m->gz4 << 4)),
int(m->bz0 | (m->bz1 << 1) | (m->bz2 << 2) | (m->bz3 << 3) | (m->bz4 << 4)));
if (bSigned)
{
e0_A.x = SIGN_EXTEND(e0_A.x, 10);
e0_A.y = SIGN_EXTEND(e0_A.y, 10);
e0_A.z = SIGN_EXTEND(e0_A.z, 10);
e0_B.x = SIGN_EXTEND(e0_B.x, 5);
e0_B.y = SIGN_EXTEND(e0_B.y, 5);
e0_B.z = SIGN_EXTEND(e0_B.z, 5);
e1_A.x = SIGN_EXTEND(e1_A.x, 5);
e1_A.y = SIGN_EXTEND(e1_A.y, 5);
e1_A.z = SIGN_EXTEND(e1_A.z, 5);
e1_B.x = SIGN_EXTEND(e1_B.x, 5);
e1_B.y = SIGN_EXTEND(e1_B.y, 5);
e1_B.z = SIGN_EXTEND(e1_B.z, 5);
}
wprintf(L"\tMode 1 - [10 5 5 5] shape %llu\n", m->d);
wprintf(L"\t E0(A): (%04X, %04X, %04X)\n", e0_A.x & 0xFFFF, e0_A.y & 0xFFFF, e0_A.z & 0xFFFF);
wprintf(L"\t E0(B): (%04X, %04X, %04X)\n", e0_B.x & 0xFFFF, e0_B.y & 0xFFFF, e0_B.z & 0xFFFF);
wprintf(L"\t E1(A): (%04X, %04X, %04X)\n", e1_A.x & 0xFFFF, e1_A.y & 0xFFFF, e1_A.z & 0xFFFF);
wprintf(L"\t E1(B): (%04X, %04X, %04X)\n", e1_B.x & 0xFFFF, e1_B.y & 0xFFFF, e1_B.z & 0xFFFF);
wprintf(L"\t Index: ");
PrintIndex3bpp(m->indices, 1, m->d);
wprintf(L"\n");
}
break;
case 0x01:
// Mode 2 (2 bits, 01)
{
struct bc6h_mode2
{
uint64_t mode : 2; // { M, 0}, { M, 1}
uint64_t gy5 : 1; // {GY, 5}
uint64_t gz45 : 2; // {GZ, 4}, {GZ, 5}
uint64_t rw : 7; // {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, {RW, 5}, {RW, 6}
uint64_t bz : 2; // {BZ, 0}, {BZ, 1}
uint64_t by4 : 1; // {BY, 4},
uint64_t gw : 7; // {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, {GW, 5}, {GW, 6}
uint64_t by5 : 1; // {BY, 5}
uint64_t bz2 : 1; // {BZ, 2}
uint64_t gy4 : 1; // {GY, 4}
uint64_t bw : 7; // {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, {BW, 5}, {BW, 6}
uint64_t bz3 : 1; // {BZ, 3}
uint64_t bz5 : 1; // {BZ, 5}
uint64_t bz4 : 1; // {BZ, 4}
uint64_t rx : 6; // {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}, {RX, 4}, {RX, 5}
uint64_t gy : 4; // {GY, 0}, {GY, 1}, {GY, 2}, {GY, 3}
uint64_t gx : 6; // {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}, {GX, 4}, {GX, 5}
uint64_t gz : 4; // {GZ, 0}, {GZ, 1}, {GZ, 2}, {GZ, 3}
uint64_t bx : 5; // {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}, {BX, 4}, {BX, 5}
uint64_t by : 4; // {BY, 0}, {BY, 1}, {BY, 2}, {BY, 3}
uint64_t ry : 6; // {RY, 0}, {RY, 1}, {RY, 2}, {RY, 3}, {RY, 4}, {RY, 5}
uint64_t rz : 6; // {RZ, 0}, {RZ, 1}, {RZ, 2}, {RZ, 3}, {RZ, 4}, {RZ, 5},
uint64_t d : 5; // { D, 0}, { D, 1}, { D, 2}, { D, 3}, { D, 4}
uint64_t indices : 46;
};
static_assert(sizeof(bc6h_mode2) == 16, "Block size must be 16 bytes");
const bool bSigned = (image.format == DXGI_FORMAT_BC6H_SF16) ? true : false;
auto m = reinterpret_cast<const bc6h_mode2*>(sptr);
XMINT3 e0_A(int(m->rw), int(m->gw), int(m->bw));
XMINT3 e0_B(int(m->rx), int(m->gx), int(m->bx));
XMINT3 e1_A(int(m->ry),
int(m->gy | (m->gy4 << 4) | (m->gy5 << 5)),
int(m->by | (m->by4 << 4) | (m->by5 << 5)));
XMINT3 e1_B(int(m->rz),
int(m->gz | (m->gz45 << 4)),
int(m->bz | (m->bz2 << 2) | (m->bz3 << 3) | (m->bz4 << 4) | (m->bz5 << 5)));
if (bSigned)
{
e0_A.x = SIGN_EXTEND(e0_A.x, 7);
e0_A.y = SIGN_EXTEND(e0_A.y, 7);
e0_A.z = SIGN_EXTEND(e0_A.z, 7);
e0_B.x = SIGN_EXTEND(e0_B.x, 6);
e0_B.y = SIGN_EXTEND(e0_B.y, 6);
e0_B.z = SIGN_EXTEND(e0_B.z, 6);
e1_A.x = SIGN_EXTEND(e1_A.x, 6);
e1_A.y = SIGN_EXTEND(e1_A.y, 6);
e1_A.z = SIGN_EXTEND(e1_A.z, 6);
e1_B.x = SIGN_EXTEND(e1_B.x, 6);
e1_B.y = SIGN_EXTEND(e1_B.y, 6);
e1_B.z = SIGN_EXTEND(e1_B.z, 6);
}
wprintf(L"\tMode 2 - [7 6 6 6] shape %llu\n", m->d);
wprintf(L"\t E0(A): (%04X, %04X, %04X)\n", e0_A.x & 0xFFFF, e0_A.y & 0xFFFF, e0_A.z & 0xFFFF);
wprintf(L"\t E0(B): (%04X, %04X, %04X)\n", e0_B.x & 0xFFFF, e0_B.y & 0xFFFF, e0_B.z & 0xFFFF);
wprintf(L"\t E1(A): (%04X, %04X, %04X)\n", e1_A.x & 0xFFFF, e1_A.y & 0xFFFF, e1_A.z & 0xFFFF);
wprintf(L"\t E1(B): (%04X, %04X, %04X)\n", e1_B.x & 0xFFFF, e1_B.y & 0xFFFF, e1_B.z & 0xFFFF);
wprintf(L"\t Index: ");
PrintIndex3bpp(m->indices, 1, m->d);
wprintf(L"\n");
}
break;
default:
switch (*sptr & 0x1F)
{
case 0x02:
// Mode 3 (5 bits, 00010)
{
struct bc6h_mode3
{
uint64_t mode : 5; // { M, 0}, { M, 1}, { M, 2}, { M, 3}, { M, 4}
uint64_t rw : 10; // {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, {RW, 5}, {RW, 6}, {RW, 7}, {RW, 8}, {RW, 9}
uint64_t gw : 10; // {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, {GW, 5}, {GW, 6}, {GW, 7}, {GW, 8}, {GW, 9}
uint64_t bw : 10; // {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, {BW, 5}, {BW, 6}, {BW, 7}, {BW, 8}, {BW, 9}
uint64_t rx : 5; // {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}, {RX, 4}
uint64_t rw10 : 1; // {RW,10}
uint64_t gy : 4; // {GY, 0}, {GY, 1}, {GY, 2}, {GY, 3}
uint64_t gx : 4; // {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}
uint64_t gw10 : 1; // {GW,10}
uint64_t bz0 : 1; // {BZ, 0}
uint64_t gz : 4; // {GZ, 0}, {GZ, 1}, {GZ, 2}, {GZ, 3}
uint64_t bx : 4; // {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}
uint64_t bw10 : 1; // {BW,10}
uint64_t bz1 : 1; // {BZ, 1}
uint64_t by : 3; // {BY, 0}, {BY, 1}, {BY, 2}
uint64_t by3 : 1; // {BY, 3}
uint64_t ry : 5; // {RY, 0}, {RY, 1}, {RY, 2}, {RY, 3}, {RY, 4}
uint64_t bz2 : 1; // {BZ, 2}
uint64_t rz : 5; // {RZ, 0}, {RZ, 1}, {RZ, 2}, {RZ, 3}, {RZ, 4}
uint64_t bz3 : 1; // {BZ, 3}
uint64_t d : 5; // { D, 0}, { D, 1}, { D, 2}, { D, 3}, { D, 4}
uint64_t indices : 46;
};
static_assert(sizeof(bc6h_mode3) == 16, "Block size must be 16 bytes");
const bool bSigned = (image.format == DXGI_FORMAT_BC6H_SF16) ? true : false;
auto m = reinterpret_cast<const bc6h_mode3*>(sptr);
XMINT3 e0_A(int(m->rw | (m->rw10 << 10)),
int(m->gw | (m->gw10 << 10)),
int(m->bw | (m->bw10 << 10)));
XMINT3 e0_B(int(m->rx), int(m->gx), int(m->bx));
XMINT3 e1_A(int(m->ry), int(m->gy),
int(m->by | (m->by3 << 3)));
XMINT3 e1_B(int(m->rz),
int(m->gz),
int(m->bz0 | (m->bz1 << 1) | (m->bz2 << 2) | (m->bz3 << 3)));
if (bSigned)
{
e0_A.x = SIGN_EXTEND(e0_A.x, 11);
e0_A.y = SIGN_EXTEND(e0_A.y, 11);
e0_A.z = SIGN_EXTEND(e0_A.z, 11);
e0_B.x = SIGN_EXTEND(e0_B.x, 5);
e0_B.y = SIGN_EXTEND(e0_B.y, 4);
e0_B.z = SIGN_EXTEND(e0_B.z, 4);
e1_A.x = SIGN_EXTEND(e1_A.x, 5);
e1_A.y = SIGN_EXTEND(e1_A.y, 4);
e1_A.z = SIGN_EXTEND(e1_A.z, 4);
e1_B.x = SIGN_EXTEND(e1_B.x, 5);
e1_B.y = SIGN_EXTEND(e1_B.y, 4);
e1_B.z = SIGN_EXTEND(e1_B.z, 4);
}
wprintf(L"\tMode 3 - [11 5 4 4] shape %llu\n", m->d);
wprintf(L"\t E0(A): (%04X, %04X, %04X)\n", e0_A.x & 0xFFFF, e0_A.y & 0xFFFF, e0_A.z & 0xFFFF);
wprintf(L"\t E0(B): (%04X, %04X, %04X)\n", e0_B.x & 0xFFFF, e0_B.y & 0xFFFF, e0_B.z & 0xFFFF);
wprintf(L"\t E1(A): (%04X, %04X, %04X)\n", e1_A.x & 0xFFFF, e1_A.y & 0xFFFF, e1_A.z & 0xFFFF);
wprintf(L"\t E1(B): (%04X, %04X, %04X)\n", e1_B.x & 0xFFFF, e1_B.y & 0xFFFF, e1_B.z & 0xFFFF);
wprintf(L"\t Index: ");
PrintIndex3bpp(m->indices, 1, m->d);
wprintf(L"\n");
}
break;
case 0x06:
// Mode 4 (5 bits, 00110)
{
struct bc6h_mode4
{
uint64_t mode : 5; // { M, 0}, { M, 1}, { M, 2}, { M, 3}, { M, 4}
uint64_t rw : 10; // {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, {RW, 5}, {RW, 6}, {RW, 7}, {RW, 8}, {RW, 9}
uint64_t gw : 10; // {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, {GW, 5}, {GW, 6}, {GW, 7}, {GW, 8}, {GW, 9}
uint64_t bw : 10; // {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, {BW, 5}, {BW, 6}, {BW, 7}, {BW, 8}, {BW, 9}
uint64_t rx : 4; // {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}
uint64_t rw10 : 1; // {RW,10}
uint64_t gz4 : 1; // {GZ, 4}
uint64_t gy : 4; // {GY, 0}, {GY, 1}, {GY, 2}, {GY, 3}
uint64_t gx : 5; // {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}, {GX, 4}
uint64_t gw10 : 1; // {GW,10}
uint64_t gz : 4; // {GZ, 0}, {GZ, 1}, {GZ, 2}, {GZ, 3}
uint64_t bx : 4; // {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}
uint64_t bw10 : 1; // {BW,10}
uint64_t bz1 : 1; // {BZ, 1}
uint64_t by : 3; // {BY, 0}, {BY, 1}, {BY, 2}
uint64_t by3 : 1; // {BY, 3}
uint64_t ry : 4; // {RY, 0}, {RY, 1}, {RY, 2}, {RY, 3}
uint64_t bz0 : 1; // {BZ, 0}
uint64_t bz2 : 1; // {BZ, 2}
uint64_t rz : 4; // {RZ, 0}, {RZ, 1}, {RZ, 2}, {RZ, 3}
uint64_t gy4 : 1; // {GY, 4}
uint64_t bz3 : 1; // {BZ, 3}
uint64_t d : 5; // { D, 0}, { D, 1}, { D, 2}, { D, 3}, { D, 4}
uint64_t indices : 46;
};
static_assert(sizeof(bc6h_mode4) == 16, "Block size must be 16 bytes");
const bool bSigned = (image.format == DXGI_FORMAT_BC6H_SF16) ? true : false;
auto m = reinterpret_cast<const bc6h_mode4*>(sptr);
XMINT3 e0_A(int(m->rw | (m->rw10 << 10)),
int(m->gw | (m->gw10 << 10)),
int(m->bw | (m->bw10 << 10)));
XMINT3 e0_B(int(m->rx), int(m->gx), int(m->bx));
XMINT3 e1_A(int(m->ry),
int(m->gy | (m->gy4 << 4)),
int(m->by | (m->by3 << 3)));
XMINT3 e1_B(int(m->rz),
int(m->gz | (m->gz4 << 4)),
int(m->bz0 | (m->bz1 << 1) | (m->bz2 << 2) | (m->bz3 << 3)));
if (bSigned)
{
e0_A.x = SIGN_EXTEND(e0_A.x, 11);
e0_A.y = SIGN_EXTEND(e0_A.y, 11);
e0_A.z = SIGN_EXTEND(e0_A.z, 11);
e0_B.x = SIGN_EXTEND(e0_B.x, 4);
e0_B.y = SIGN_EXTEND(e0_B.y, 5);
e0_B.z = SIGN_EXTEND(e0_B.z, 4);
e1_A.x = SIGN_EXTEND(e1_A.x, 4);
e1_A.y = SIGN_EXTEND(e1_A.y, 5);
e1_A.z = SIGN_EXTEND(e1_A.z, 4);
e1_B.x = SIGN_EXTEND(e1_B.x, 4);
e1_B.y = SIGN_EXTEND(e1_B.y, 5);
e1_B.z = SIGN_EXTEND(e1_B.z, 4);
}
wprintf(L"\tMode 4 - [11 4 5 4] shape %llu\n", m->d);
wprintf(L"\t E0(A): (%04X, %04X, %04X)\n", e0_A.x & 0xFFFF, e0_A.y & 0xFFFF, e0_A.z & 0xFFFF);
wprintf(L"\t E0(B): (%04X, %04X, %04X)\n", e0_B.x & 0xFFFF, e0_B.y & 0xFFFF, e0_B.z & 0xFFFF);
wprintf(L"\t E1(A): (%04X, %04X, %04X)\n", e1_A.x & 0xFFFF, e1_A.y & 0xFFFF, e1_A.z & 0xFFFF);
wprintf(L"\t E1(B): (%04X, %04X, %04X)\n", e1_B.x & 0xFFFF, e1_B.y & 0xFFFF, e1_B.z & 0xFFFF);
wprintf(L"\t Index: ");
PrintIndex3bpp(m->indices, 1, m->d);
wprintf(L"\n");
}
break;
case 0x0A:
// Mode 5 (5 bits, 01010)
{
struct bc6h_mode5
{
uint64_t mode : 5; // { M, 0}, { M, 1}, { M, 2}, { M, 3}, { M, 4}
uint64_t rw : 10; // {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, {RW, 5}, {RW, 6}, {RW, 7}, {RW, 8}, {RW, 9}
uint64_t gw : 10; // {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, {GW, 5}, {GW, 6}, {GW, 7}, {GW, 8}, {GW, 9}
uint64_t bw : 10; // {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, {BW, 5}, {BW, 6}, {BW, 7}, {BW, 8}, {BW, 9}
uint64_t rx : 4; // {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}
uint64_t rw10 : 1; // {RW,10}
uint64_t by4 : 1; // {BY, 4}
uint64_t gy : 4; // {GY, 0}, {GY, 1}, {GY, 2}, {GY, 3}
uint64_t gx : 4; // {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}
uint64_t gw10 : 1; // {GW,10}
uint64_t bz0 : 1; // {BZ, 0}
uint64_t gz : 4; // {GZ, 0}, {GZ, 1}, {GZ, 2}, {GZ, 3}
uint64_t bx : 5; // {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}, {BX, 4}
uint64_t bw10 : 1; // {BW,10}
uint64_t by : 3; // {BY, 0}, {BY, 1}, {BY, 2}
uint64_t by3 : 1; // {BY, 3}
uint64_t ry : 4; // {RY, 0}, {RY, 1}, {RY, 2}, {RY, 3}
uint64_t bz12 : 2; // {BZ, 1}, {BZ, 2}
uint64_t rz : 5; // {RZ, 0}, {RZ, 1}, {RZ, 2}, {RZ, 3}, {BZ, 4}
uint64_t bz3 : 1; // {BZ, 3}
uint64_t d : 5; // { D, 0}, { D, 1}, { D, 2}, { D, 3}, { D, 4}
uint64_t indices : 46;
};
static_assert(sizeof(bc6h_mode5) == 16, "Block size must be 16 bytes");
const bool bSigned = (image.format == DXGI_FORMAT_BC6H_SF16) ? true : false;
auto m = reinterpret_cast<const bc6h_mode5*>(sptr);
XMINT3 e0_A(int(m->rw | (m->rw10 << 10)),
int(m->gw | (m->gw10 << 10)),
int(m->bw | (m->bw10 << 10)));
XMINT3 e0_B(int(m->rx), int(m->gx), int(m->bx));
XMINT3 e1_A(int(m->ry), int(m->gy),
int(m->by | (m->by3 << 3) | (m->by4 << 4)));
XMINT3 e1_B(int(m->rz), int(m->gz),
int(m->bz0 | (m->bz12 << 1) | (m->bz3 << 3)));
if (bSigned)
{
e0_A.x = SIGN_EXTEND(e0_A.x, 11);
e0_A.y = SIGN_EXTEND(e0_A.y, 11);
e0_A.z = SIGN_EXTEND(e0_A.z, 11);
e0_B.x = SIGN_EXTEND(e0_B.x, 4);
e0_B.y = SIGN_EXTEND(e0_B.y, 4);
e0_B.z = SIGN_EXTEND(e0_B.z, 5);
e1_A.x = SIGN_EXTEND(e1_A.x, 4);
e1_A.y = SIGN_EXTEND(e1_A.y, 4);
e1_A.z = SIGN_EXTEND(e1_A.z, 5);
e1_B.x = SIGN_EXTEND(e1_B.x, 4);
e1_B.y = SIGN_EXTEND(e1_B.y, 4);
e1_B.z = SIGN_EXTEND(e1_B.z, 5);
}
wprintf(L"\tMode 5 - [11 4 4 5] shape %llu\n", m->d);
wprintf(L"\t E0(A): (%04X, %04X, %04X)\n", e0_A.x & 0xFFFF, e0_A.y & 0xFFFF, e0_A.z & 0xFFFF);
wprintf(L"\t E0(B): (%04X, %04X, %04X)\n", e0_B.x & 0xFFFF, e0_B.y & 0xFFFF, e0_B.z & 0xFFFF);
wprintf(L"\t E1(A): (%04X, %04X, %04X)\n", e1_A.x & 0xFFFF, e1_A.y & 0xFFFF, e1_A.z & 0xFFFF);
wprintf(L"\t E1(B): (%04X, %04X, %04X)\n", e1_B.x & 0xFFFF, e1_B.y & 0xFFFF, e1_B.z & 0xFFFF);
wprintf(L"\t Index: ");
PrintIndex3bpp(m->indices, 1, m->d);
wprintf(L"\n");
}
break;
case 0x0E:
// Mode 6 (5 bits, 01110)
{
struct bc6h_mode6
{
uint64_t mode : 5; // { M, 0}, { M, 1}, { M, 2}, { M, 3}, { M, 4}
uint64_t rw : 9; // {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, {RW, 5}, {RW, 6}, {RW, 7}, {RW, 8}
uint64_t by4 : 1; // {BY, 4}
uint64_t gw : 9; // {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, {GW, 5}, {GW, 6}, {GW, 7}, {GW, 8}
uint64_t gy4 : 1; // {GY, 4}
uint64_t bw : 9; // {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, {BW, 5}, {BW, 6}, {BW, 7}, {BW, 8}
uint64_t bz4 : 1; // {BZ, 4}
uint64_t rx : 5; // {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}, {RX, 4}
uint64_t gz4 : 1; // {GZ, 4}
uint64_t gy : 4; // {GY, 0}, {GY, 1}, {GY, 2}, {GY, 3}
uint64_t gx : 5; // {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}, {GX, 4}
uint64_t bz0 : 1; // {BZ, 0}
uint64_t gz : 4; // {GZ, 0}, {GZ, 1}, {GZ, 2}, {GZ, 3}
uint64_t bx : 5; // {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}, {BX, 4}
uint64_t bz1 : 1; // {BZ, 1}
uint64_t by : 3; // {BY, 0}, {BY, 1}, {BY, 2}
uint64_t by3 : 1; // {BY, 3}
uint64_t ry : 5; // {RY, 0}, {RY, 1}, {RY, 2}, {RY, 3}, {RY, 4},
uint64_t bz2 : 1; // {BZ, 2}
uint64_t rz : 5; // {RZ, 0}, {RZ, 1}, {RZ, 2}, {RZ, 3}, {BZ, 4}
uint64_t bz3 : 1; // {BZ, 3}
uint64_t d : 5; // { D, 0}, { D, 1}, { D, 2}, { D, 3}, { D, 4}
uint64_t indices : 46;
};
static_assert(sizeof(bc6h_mode6) == 16, "Block size must be 16 bytes");
const bool bSigned = (image.format == DXGI_FORMAT_BC6H_SF16) ? true : false;
auto m = reinterpret_cast<const bc6h_mode6*>(sptr);
XMINT3 e0_A(int(m->rw), int(m->gw), int(m->bw));
XMINT3 e0_B(int(m->rx), int(m->gx), int(m->bx));
XMINT3 e1_A(int(m->ry),
int(m->gy | (m->gy4 << 4)),
int(m->by | (m->by3 << 3) | (m->by4 << 4)));
XMINT3 e1_B(int(m->rz),
int(m->gz | (m->gz4 << 4)),
int(m->bz0 | (m->bz1 << 1) | (m->bz2 << 2) | (m->bz3 << 3) | (m->bz4 << 4)));
if (bSigned)
{
e0_A.x = SIGN_EXTEND(e0_A.x, 9);
e0_A.y = SIGN_EXTEND(e0_A.y, 9);
e0_A.z = SIGN_EXTEND(e0_A.z, 9);
e0_B.x = SIGN_EXTEND(e0_B.x, 5);
e0_B.y = SIGN_EXTEND(e0_B.y, 5);
e0_B.z = SIGN_EXTEND(e0_B.z, 5);
e1_A.x = SIGN_EXTEND(e1_A.x, 5);
e1_A.y = SIGN_EXTEND(e1_A.y, 5);
e1_A.z = SIGN_EXTEND(e1_A.z, 5);
e1_B.x = SIGN_EXTEND(e1_B.x, 5);
e1_B.y = SIGN_EXTEND(e1_B.y, 5);
e1_B.z = SIGN_EXTEND(e1_B.z, 5);
}
wprintf(L"\tMode 6 - [9 5 5 5] shape %llu\n", m->d);
wprintf(L"\t E0(A): (%04X, %04X, %04X)\n", e0_A.x & 0xFFFF, e0_A.y & 0xFFFF, e0_A.z & 0xFFFF);
wprintf(L"\t E0(B): (%04X, %04X, %04X)\n", e0_B.x & 0xFFFF, e0_B.y & 0xFFFF, e0_B.z & 0xFFFF);
wprintf(L"\t E1(A): (%04X, %04X, %04X)\n", e1_A.x & 0xFFFF, e1_A.y & 0xFFFF, e1_A.z & 0xFFFF);
wprintf(L"\t E1(B): (%04X, %04X, %04X)\n", e1_B.x & 0xFFFF, e1_B.y & 0xFFFF, e1_B.z & 0xFFFF);
wprintf(L"\t Index: ");
PrintIndex3bpp(m->indices, 1, m->d);
wprintf(L"\n");
}
break;
case 0x12:
// Mode 7 (5 bits, 10010)
{
struct bc6h_mode7
{
uint64_t mode : 5; // { M, 0}, { M, 1}, { M, 2}, { M, 3}, { M, 4}
uint64_t rw : 8; // {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, {RW, 5}, {RW, 6}, {RW, 7}
uint64_t gz4 : 1; // {GZ, 4}
uint64_t by4 : 1; // {BY, 4}
uint64_t gw : 8; // {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, {GW, 5}, {GW, 6}, {GW, 7}
uint64_t bz2 : 1; // {BZ, 2}
uint64_t gy4 : 1; // {GY, 4}
uint64_t bw : 8; // {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, {BW, 5}, {BW, 6}, {BW, 7}
uint64_t bz3 : 1; // {BZ, 3}
uint64_t bz4 : 1; // {BZ, 4}
uint64_t rx : 6; // {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}, {RX, 4}, {RX, 5}
uint64_t gy : 4; // {GY, 0}, {GY, 1}, {GY, 2}, {GY, 3}
uint64_t gx : 5; // {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}, {GX, 4}
uint64_t bz0 : 1; // {BZ, 0}
uint64_t gz : 4; // {GZ, 0}, {GZ, 1}, {GZ, 2}, {GZ, 3}
uint64_t bx : 5; // {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}, {BX, 4}
uint64_t bz1 : 1; // {BZ, 1}
uint64_t by : 3; // {BY, 0}, {BY, 1}, {BY, 2}
uint64_t by3 : 1; // {BY, 3}
uint64_t ry : 6; // {RY, 0}, {RY, 1}, {RY, 2}, {RY, 3}, {RY, 4}, {RY, 5}
uint64_t rz : 6; // {RZ, 0}, {RZ, 1}, {RZ, 2}, {RZ, 3}, {RZ, 4}, {RZ, 5}
uint64_t d : 5; // { D, 0}, { D, 1}, { D, 2}, { D, 3}, { D, 4}
uint64_t indices : 46;
};
static_assert(sizeof(bc6h_mode7) == 16, "Block size must be 16 bytes");
const bool bSigned = (image.format == DXGI_FORMAT_BC6H_SF16) ? true : false;
auto m = reinterpret_cast<const bc6h_mode7*>(sptr);
XMINT3 e0_A(int(m->rw), int(m->gw), int(m->bw));
XMINT3 e0_B(int(m->rx), int(m->gx), int(m->bx));
XMINT3 e1_A(int(m->ry),
int(m->gy | (m->gy4 << 4)),
int(m->by | (m->by3 << 3) | (m->by4 << 4)));
XMINT3 e1_B(int(m->rz),
int(m->gz | (m->gz4 << 4)),
int(m->bz0 | (m->bz1 << 1) | (m->bz2 << 2) | (m->bz3 << 3) | (m->bz4 << 4)));
if (bSigned)
{
e0_A.x = SIGN_EXTEND(e0_A.x, 8);
e0_A.y = SIGN_EXTEND(e0_A.y, 8);
e0_A.z = SIGN_EXTEND(e0_A.z, 8);
e0_B.x = SIGN_EXTEND(e0_B.x, 6);
e0_B.y = SIGN_EXTEND(e0_B.y, 5);
e0_B.z = SIGN_EXTEND(e0_B.z, 5);
e1_A.x = SIGN_EXTEND(e1_A.x, 6);
e1_A.y = SIGN_EXTEND(e1_A.y, 5);
e1_A.z = SIGN_EXTEND(e1_A.z, 5);
e1_B.x = SIGN_EXTEND(e1_B.x, 6);
e1_B.y = SIGN_EXTEND(e1_B.y, 5);
e1_B.z = SIGN_EXTEND(e1_B.z, 5);
}
wprintf(L"\tMode 7 - [8 6 5 5] shape %llu\n", m->d);
wprintf(L"\t E0(A): (%04X, %04X, %04X)\n", e0_A.x & 0xFFFF, e0_A.y & 0xFFFF, e0_A.z & 0xFFFF);
wprintf(L"\t E0(B): (%04X, %04X, %04X)\n", e0_B.x & 0xFFFF, e0_B.y & 0xFFFF, e0_B.z & 0xFFFF);
wprintf(L"\t E1(A): (%04X, %04X, %04X)\n", e1_A.x & 0xFFFF, e1_A.y & 0xFFFF, e1_A.z & 0xFFFF);
wprintf(L"\t E1(B): (%04X, %04X, %04X)\n", e1_B.x & 0xFFFF, e1_B.y & 0xFFFF, e1_B.z & 0xFFFF);
wprintf(L"\t Index: ");
PrintIndex3bpp(m->indices, 1, m->d);
wprintf(L"\n");
}
break;
case 0x16:
// Mode 8 (5 bits, 10110)
{
struct bc6h_mode8
{
uint64_t mode : 5; // { M, 0}, { M, 1}, { M, 2}, { M, 3}, { M, 4}
uint64_t rw : 8; // {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, {RW, 5}, {RW, 6}, {RW, 7}
uint64_t bz0 : 1; // {BZ, 0}
uint64_t by4 : 1; // {BY, 4}
uint64_t gw : 8; // {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, {GW, 5}, {GW, 6}, {GW, 7}
uint64_t gy5 : 1; // {GY, 5}
uint64_t gy4 : 1; // {GY, 4}
uint64_t bw : 8; // {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, {BW, 5}, {BW, 6}, {BW, 7}
uint64_t gz5 : 1; // {GZ, 5}
uint64_t bz4 : 1; // {BZ, 4}
uint64_t rx : 5; // {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}, {RX, 4}
uint64_t gz4 : 1; // {GZ, 4}
uint64_t gy : 4; // {GY, 0}, {GY, 1}, {GY, 2}, {GY, 3}
uint64_t gx : 6; // {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}, {GX, 4}, {GX, 5}
uint64_t gz : 4; // {GZ, 0}, {GZ, 1}, {GZ, 2}, {GZ, 3}
uint64_t bx : 5; // {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}, {BX, 4}
uint64_t bz1 : 1; // {BZ, 1}
uint64_t by : 3; // {BY, 0}, {BY, 1}, {BY, 2}
uint64_t by3 : 1; // {BY, 3}
uint64_t ry : 5; // {RY, 0}, {RY, 1}, {RY, 2}, {RY, 3}, {RY, 4}
uint64_t bz2 : 1; // {BZ, 2}
uint64_t rz : 5; // {RZ, 0}, {RZ, 1}, {RZ, 2}, {RZ, 3}, {RZ, 4}
uint64_t bz3 : 1; // {BZ, 3}
uint64_t d : 5; // { D, 0}, { D, 1}, { D, 2}, { D, 3}, { D, 4}
uint64_t indices : 46;
};
static_assert(sizeof(bc6h_mode8) == 16, "Block size must be 16 bytes");
const bool bSigned = (image.format == DXGI_FORMAT_BC6H_SF16) ? true : false;
auto m = reinterpret_cast<const bc6h_mode8*>(sptr);
XMINT3 e0_A(int(m->rw), int(m->gw), int(m->bw));
XMINT3 e0_B(int(m->rx), int(m->gx), int(m->bx));
XMINT3 e1_A(int(m->ry),
int(m->gy | (m->gy4 << 4) | (m->gy5 << 5)),
int(m->by | (m->by3 << 3) | (m->by4 << 4)));
XMINT3 e1_B(int(m->rz),
int(m->gz | (m->gz4 << 4) | (m->gz5 << 5)),
int(m->bz0 | (m->bz1 << 1) | (m->bz2 << 2) | (m->bz3 << 3) | (m->bz4 << 4)));
if (bSigned)
{
e0_A.x = SIGN_EXTEND(e0_A.x, 8);
e0_A.y = SIGN_EXTEND(e0_A.y, 8);
e0_A.z = SIGN_EXTEND(e0_A.z, 8);
e0_B.x = SIGN_EXTEND(e0_B.x, 5);
e0_B.y = SIGN_EXTEND(e0_B.y, 6);
e0_B.z = SIGN_EXTEND(e0_B.z, 5);
e1_A.x = SIGN_EXTEND(e1_A.x, 5);
e1_A.y = SIGN_EXTEND(e1_A.y, 6);
e1_A.z = SIGN_EXTEND(e1_A.z, 5);
e1_B.x = SIGN_EXTEND(e1_B.x, 5);
e1_B.y = SIGN_EXTEND(e1_B.y, 6);
e1_B.z = SIGN_EXTEND(e1_B.z, 5);
}
wprintf(L"\tMode 8 - [8 5 6 5] shape %llu\n", m->d);
wprintf(L"\t E0(A): (%04X, %04X, %04X)\n", e0_A.x & 0xFFFF, e0_A.y & 0xFFFF, e0_A.z & 0xFFFF);
wprintf(L"\t E0(B): (%04X, %04X, %04X)\n", e0_B.x & 0xFFFF, e0_B.y & 0xFFFF, e0_B.z & 0xFFFF);
wprintf(L"\t E1(A): (%04X, %04X, %04X)\n", e1_A.x & 0xFFFF, e1_A.y & 0xFFFF, e1_A.z & 0xFFFF);
wprintf(L"\t E1(B): (%04X, %04X, %04X)\n", e1_B.x & 0xFFFF, e1_B.y & 0xFFFF, e1_B.z & 0xFFFF);
wprintf(L"\t Index: ");
PrintIndex3bpp(m->indices, 1, m->d);
wprintf(L"\n");
}
break;
case 0x1A:
// Mode 9 (5 bits, 11010)
{
struct bc6h_mode9
{
uint64_t mode : 5; // { M, 0}, { M, 1}, { M, 2}, { M, 3}, { M, 4}
uint64_t rw : 8; // {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, {RW, 5}, {RW, 6}, {RW, 7}
uint64_t bz1 : 1; // {BZ, 1}
uint64_t by4 : 1; // {BY, 4}
uint64_t gw : 8; // {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, {GW, 5}, {GW, 6}, {GW, 7}
uint64_t by5 : 1; // {BY, 5}
uint64_t gy4 : 1; // {GY, 4}
uint64_t bw : 8; // {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, {BW, 5}, {BW, 6}, {BW, 7}
uint64_t bz5 : 1; // {BZ, 5}
uint64_t bz4 : 1; // {BZ, 4}
uint64_t rx : 5; // {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}, {RX, 4}
uint64_t gz4 : 1; // {GZ, 4}
uint64_t gy : 4; // {GY, 0}, {GY, 1}, {GY, 2}, {GY, 3}
uint64_t gx : 5; // {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}, {GX, 4}
uint64_t bz0 : 1; // {BZ, 0}
uint64_t gz : 4; // {GZ, 0}, {GZ, 1}, {GZ, 2}, {GZ, 3}
uint64_t bx : 6; // {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}, {BX, 4}, {BX, 5}
uint64_t by : 3; // {BY, 0}, {BY, 1}, {BY, 2}
uint64_t by3 : 1; // {BY, 3}
uint64_t ry : 5; // {RY, 0}, {RY, 1}, {RY, 2}, {RY, 3}, {RY, 4}
uint64_t bz2 : 1; // {BZ, 2}
uint64_t rz : 5; // {RZ, 0}, {RZ, 1}, {RZ, 2}, {RZ, 3}, {RZ, 4}
uint64_t bz3 : 1; // {BZ, 3}
uint64_t d : 5; // { D, 0}, { D, 1}, { D, 2}, { D, 3}, { D, 4}
uint64_t indices : 46;
};
static_assert(sizeof(bc6h_mode9) == 16, "Block size must be 16 bytes");
const bool bSigned = (image.format == DXGI_FORMAT_BC6H_SF16) ? true : false;
auto m = reinterpret_cast<const bc6h_mode9*>(sptr);
XMINT3 e0_A(int(m->rw), int(m->gw), int(m->bw));
XMINT3 e0_B(int(m->rx), int(m->gx), int(m->bx));
XMINT3 e1_A(int(m->ry),
int(m->gy | (m->gy4 << 4)),
int(m->by | (m->by3 << 3) | (m->by4 << 4) | (m->by5 << 5)));
XMINT3 e1_B(int(m->rz),
int(m->gz | (m->gz4 << 4)),
int(m->bz0 | (m->bz1 << 1) | (m->bz2 << 2) | (m->bz3 << 3) | (m->bz4 << 4) | (m->bz5 << 5)));
if (bSigned)
{
e0_A.x = SIGN_EXTEND(e0_A.x, 8);
e0_A.y = SIGN_EXTEND(e0_A.y, 8);
e0_A.z = SIGN_EXTEND(e0_A.z, 8);
e0_B.x = SIGN_EXTEND(e0_B.x, 5);
e0_B.y = SIGN_EXTEND(e0_B.y, 5);
e0_B.z = SIGN_EXTEND(e0_B.z, 6);
e1_A.x = SIGN_EXTEND(e1_A.x, 5);
e1_A.y = SIGN_EXTEND(e1_A.y, 5);
e1_A.z = SIGN_EXTEND(e1_A.z, 6);
e1_B.x = SIGN_EXTEND(e1_B.x, 5);
e1_B.y = SIGN_EXTEND(e1_B.y, 5);
e1_B.z = SIGN_EXTEND(e1_B.z, 6);
}
wprintf(L"\tMode 9 - [8 5 5 6] shape %llu\n", m->d);
wprintf(L"\t E0(A): (%04X, %04X, %04X)\n", e0_A.x & 0xFFFF, e0_A.y & 0xFFFF, e0_A.z & 0xFFFF);
wprintf(L"\t E0(B): (%04X, %04X, %04X)\n", e0_B.x & 0xFFFF, e0_B.y & 0xFFFF, e0_B.z & 0xFFFF);
wprintf(L"\t E1(A): (%04X, %04X, %04X)\n", e1_A.x & 0xFFFF, e1_A.y & 0xFFFF, e1_A.z & 0xFFFF);
wprintf(L"\t E1(B): (%04X, %04X, %04X)\n", e1_B.x & 0xFFFF, e1_B.y & 0xFFFF, e1_B.z & 0xFFFF);
wprintf(L"\t Index: ");
PrintIndex3bpp(m->indices, 1, m->d);
wprintf(L"\n");
}
break;
case 0x1E:
// Mode 10 (5 bits, 11110)
{
struct bc6h_mode10
{
uint64_t mode : 5; // { M, 0}, { M, 1}, { M, 2}, { M, 3}, { M, 4}
uint64_t rw : 6; // {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, {RW, 5}
uint64_t gz4 : 1; // {GZ, 4}
uint64_t bz : 2; // {BZ, 0}, {BZ, 1}
uint64_t by4 : 1; // {BY, 4}
uint64_t gw : 6; // {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, {GW, 5}
uint64_t gy5 : 1; // {GY, 5}
uint64_t by5 : 1; // {BY, 5}
uint64_t bz2 : 1; // {BZ, 2}
uint64_t gy4 : 1; // {GY, 4}
uint64_t bw : 6; // {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, {BW, 5}, {GZ, 5}
uint64_t bz3 : 1; // {BZ, 3}
uint64_t bz5 : 1; // {BZ, 5}
uint64_t bz4 : 1; // {BZ, 4}
uint64_t rx : 6; // {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}, {RX, 4}, {RX, 5}
uint64_t gy : 4; // {GY, 0}, {GY, 1}, {GY, 2}, {GY, 3}
uint64_t gx : 6; // {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}, {GX, 4}, {GX, 5}
uint64_t gz : 4; // {GZ, 0}, {GZ, 1}, {GZ, 2}, {GZ, 3}
uint64_t bx : 6; // {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}, {BX, 4}, {BX, 5}
uint64_t by : 3; // {BY, 0}, {BY, 1}, {BY, 2}
uint64_t by3 : 1; // {BY, 3}
uint64_t ry : 6; // {RY, 0}, {RY, 1}, {RY, 2}, {RY, 3}, {RY, 4}, {RY, 5}
uint64_t rz : 6; // {RZ, 0}, {RZ, 1}, {RZ, 2}, {RZ, 3}, {RZ, 4}, {RZ, 5}
uint64_t d : 5; // { D, 0}, { D, 1}, { D, 2}, { D, 3}, { D, 4}
uint64_t indices : 46;
};
static_assert(sizeof(bc6h_mode10) == 16, "Block size must be 16 bytes");
const bool bSigned = (image.format == DXGI_FORMAT_BC6H_SF16) ? true : false;
auto m = reinterpret_cast<const bc6h_mode10*>(sptr);
XMINT3 e0_A(int(m->rw), int(m->gw), int(m->bw));
XMINT3 e0_B(int(m->rx), int(m->gx), int(m->bx));
XMINT3 e1_A(int(m->ry),
int(m->gy | (m->gy4 << 4) | (m->gy5 << 5)),
int(m->by | (m->by3 << 3) | (m->by4 << 4) | (m->by5 << 5)));
XMINT3 e1_B(int(m->rz),
int(m->gz | (m->gz4 << 4)),
int(m->bz | (m->bz2 << 2) | (m->bz3 << 3) | (m->bz4 << 4) | (m->bz5 << 5)));
if (bSigned)
{
e0_A.x = SIGN_EXTEND(e0_A.x, 6);
e0_A.y = SIGN_EXTEND(e0_A.y, 6);
e0_A.z = SIGN_EXTEND(e0_A.z, 6);
e0_B.x = SIGN_EXTEND(e0_B.x, 6);
e0_B.y = SIGN_EXTEND(e0_B.y, 6);
e0_B.z = SIGN_EXTEND(e0_B.z, 6);
e1_A.x = SIGN_EXTEND(e1_A.x, 6);
e1_A.y = SIGN_EXTEND(e1_A.y, 6);
e1_A.z = SIGN_EXTEND(e1_A.z, 6);
e1_B.x = SIGN_EXTEND(e1_B.x, 6);
e1_B.y = SIGN_EXTEND(e1_B.y, 6);
e1_B.z = SIGN_EXTEND(e1_B.z, 6);
}
wprintf(L"\tMode 10 - [6 6 6 6] shape %llu\n", m->d);
wprintf(L"\t E0(A): (%04X, %04X, %04X)\n", e0_A.x & 0xFFFF, e0_A.y & 0xFFFF, e0_A.z & 0xFFFF);
wprintf(L"\t E0(B): (%04X, %04X, %04X)\n", e0_B.x & 0xFFFF, e0_B.y & 0xFFFF, e0_B.z & 0xFFFF);
wprintf(L"\t E1(A): (%04X, %04X, %04X)\n", e1_A.x & 0xFFFF, e1_A.y & 0xFFFF, e1_A.z & 0xFFFF);
wprintf(L"\t E1(B): (%04X, %04X, %04X)\n", e1_B.x & 0xFFFF, e1_B.y & 0xFFFF, e1_B.z & 0xFFFF);
wprintf(L"\t Index: ");
PrintIndex3bpp(m->indices, 1, m->d);
wprintf(L"\n");
}
break;
case 0x03:
// Mode 11 (5 bits, 00011)
{
struct bc6h_mode11
{
uint64_t mode : 5; // { M, 0}, { M, 1}, { M, 2}, { M, 3}, { M, 4}
uint64_t rw : 10; // {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, {RW, 5}, {RW, 6}, {RW, 7}, {RW, 8}, {RW, 9}
uint64_t gw : 10; // {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, {GW, 5}, {GW, 6}, {GW, 7}, {GW, 8}, {GW, 9}
uint64_t bw : 10; // {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, {BW, 5}, {BW, 6}, {BW, 7}, {BW, 8}, {BW, 9}
uint64_t rx : 10; // {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}, {RX, 4}, {RX, 5}, {RX, 6}, {RX, 7}, {RX, 8}, {RX, 9}
uint64_t gx : 10; // {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}, {GX, 4}, {GX, 5}, {GX, 6}, {GX, 7}, {GX, 8}, {GX, 9}
uint64_t bx : 9; // {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}, {BX, 4}, {BX, 5}, {BX, 6}, {BX, 7}, {BX, 8}
uint64_t bx9 : 1; // {BX, 9}
uint64_t indices : 63;
};
static_assert(sizeof(bc6h_mode11) == 16, "Block size must be 16 bytes");
const bool bSigned = (image.format == DXGI_FORMAT_BC6H_SF16) ? true : false;
auto m = reinterpret_cast<const bc6h_mode11*>(sptr);
XMINT3 e0_A(int(m->rw), int(m->gw), int(m->bw));
XMINT3 e0_B(int(m->rx), int(m->gx),
int(m->bx | (m->bx9 << 9)));
if (bSigned)
{
e0_A.x = SIGN_EXTEND(e0_A.x, 10);
e0_A.y = SIGN_EXTEND(e0_A.y, 10);
e0_A.z = SIGN_EXTEND(e0_A.z, 10);
e0_B.x = SIGN_EXTEND(e0_B.x, 10);
e0_B.y = SIGN_EXTEND(e0_B.y, 10);
e0_B.z = SIGN_EXTEND(e0_B.z, 10);
}
wprintf(L"\tMode 11 - [10 10]\n");
wprintf(L"\t E(A): (%04X, %04X, %04X)\n", e0_A.x & 0xFFFF, e0_A.y & 0xFFFF, e0_A.z & 0xFFFF);
wprintf(L"\t E(B): (%04X, %04X, %04X)\n", e0_B.x & 0xFFFF, e0_B.y & 0xFFFF, e0_B.z & 0xFFFF);
wprintf(L"\t Index: ");
PrintIndex4bpp(m->indices, 0, 0);
wprintf(L"\n");
}
break;
case 0x07:
// Mode 12 (5 bits, 00111)
{
struct bc6h_mode12
{
uint64_t mode : 5; // { M, 0}, { M, 1}, { M, 2}, { M, 3}, { M, 4}
uint64_t rw : 10; // {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, {RW, 5}, {RW, 6}, {RW, 7}, {RW, 8}, {RW, 9}
uint64_t gw : 10; // {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, {GW, 5}, {GW, 6}, {GW, 7}, {GW, 8}, {GW, 9}
uint64_t bw : 10; // {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, {BW, 5}, {BW, 6}, {BW, 7}, {BW, 8}, {BW, 9}
uint64_t rx : 9; // {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}, {RX, 4}, {RX, 5}, {RX, 6}, {RX, 7}, {RX, 8}
uint64_t rw10 : 1; // {RW,10}
uint64_t gx : 9; // {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}, {GX, 4}, {GX, 5}, {GX, 6}, {GX, 7}, {GX, 8}
uint64_t gw10 : 1; // {GW,10}
uint64_t bx : 9; // {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}, {BX, 4}, {BX, 5}, {BX, 6}, {BX, 7}, {BX, 8}
uint64_t bw10 : 1; // {BW,10}
uint64_t indices : 63;
};
static_assert(sizeof(bc6h_mode12) == 16, "Block size must be 16 bytes");
const bool bSigned = (image.format == DXGI_FORMAT_BC6H_SF16) ? true : false;
auto m = reinterpret_cast<const bc6h_mode12*>(sptr);
XMINT3 e0_A(int(m->rw | (m->rw10 << 10)),
int(m->gw | (m->gw10 << 10)),
int(m->bw | (m->bw10 << 10)));
XMINT3 e0_B(int(m->rx), int(m->gx), int(m->bx));
if (bSigned)
{
e0_A.x = SIGN_EXTEND(e0_A.x, 11);
e0_A.y = SIGN_EXTEND(e0_A.y, 11);
e0_A.z = SIGN_EXTEND(e0_A.z, 11);
e0_B.x = SIGN_EXTEND(e0_B.x, 9);
e0_B.y = SIGN_EXTEND(e0_B.y, 9);
e0_B.z = SIGN_EXTEND(e0_B.z, 9);
}
wprintf(L"\tMode 12 - [11 9]\n");
wprintf(L"\t E(A): (%04X, %04X, %04X)\n", e0_A.x & 0xFFFF, e0_A.y & 0xFFFF, e0_A.z & 0xFFFF);
wprintf(L"\t E(B): (%04X, %04X, %04X)\n", e0_B.x & 0xFFFF, e0_B.y & 0xFFFF, e0_B.z & 0xFFFF);
wprintf(L"\t Index: ");
PrintIndex4bpp(m->indices, 0, 0);
wprintf(L"\n");
}
break;
case 0x0B:
// Mode 13 (5 bits, 01011)
{
struct bc6h_mode13
{
uint64_t mode : 5; // { M, 0}, { M, 1}, { M, 2}, { M, 3}, { M, 4}
uint64_t rw : 10; // {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, {RW, 5}, {RW, 6}, {RW, 7}, {RW, 8}, {RW, 9}
uint64_t gw : 10; // {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, {GW, 5}, {GW, 6}, {GW, 7}, {GW, 8}, {GW, 9}
uint64_t bw : 10; // {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, {BW, 5}, {BW, 6}, {BW, 7}, {BW, 8}, {BW, 9}
uint64_t rx : 8; // {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}, {RX, 4}, {RX, 5}, {RX, 6}, {RX, 7}
uint64_t rw11 : 1; // {RW,11}
uint64_t rw10 : 1; // {RW,10}
uint64_t gx : 8; // {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}, {GX, 4}, {GX, 5}, {GX, 6}, {GX, 7}
uint64_t gw11 : 1; // {GW,11}
uint64_t gw10 : 1; // {GW,10}
uint64_t bx : 8; // {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}, {BX, 4}, {BX, 5}, {BX, 6}, {BX, 7}
uint64_t bw11 : 1; // {BW,11}
uint64_t bw10 : 1; // {BW,10}
uint64_t indices : 63;
};
static_assert(sizeof(bc6h_mode13) == 16, "Block size must be 16 bytes");
const bool bSigned = (image.format == DXGI_FORMAT_BC6H_SF16) ? true : false;
auto m = reinterpret_cast<const bc6h_mode13*>(sptr);
XMINT3 e0_A(int(m->rw | (m->rw10 << 10) | (m->rw11 << 11)),
int(m->gw | (m->gw10 << 10) | (m->gw11 << 11)),
int(m->bw | (m->bw10 << 10) | (m->bw11 << 11)));
XMINT3 e0_B(int(m->rx), int(m->gx), int(m->bx));
if (bSigned)
{
e0_A.x = SIGN_EXTEND(e0_A.x, 12);
e0_A.y = SIGN_EXTEND(e0_A.y, 12);
e0_A.z = SIGN_EXTEND(e0_A.z, 12);
e0_B.x = SIGN_EXTEND(e0_B.x, 8);
e0_B.y = SIGN_EXTEND(e0_B.y, 8);
e0_B.z = SIGN_EXTEND(e0_B.z, 8);
}
wprintf(L"\tMode 13 - [12 8]\n");
wprintf(L"\t E(A): (%04X, %04X, %04X)\n", e0_A.x & 0xFFFF, e0_A.y & 0xFFFF, e0_A.z & 0xFFFF);
wprintf(L"\t E(B): (%04X, %04X, %04X)\n", e0_B.x & 0xFFFF, e0_B.y & 0xFFFF, e0_B.z & 0xFFFF);
wprintf(L"\t Index: ");
PrintIndex4bpp(m->indices, 0, 0);
wprintf(L"\n");
}
break;
case 0x0F:
// Mode 14 (5 bits, 01111)
{
struct bc6h_mode14
{
uint64_t mode : 5; // { M, 0}, { M, 1}, { M, 2}, { M, 3}, { M, 4}
uint64_t rw : 10; // {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, {RW, 5}, {RW, 6}, {RW, 7}, {RW, 8}, {RW, 9}
uint64_t gw : 10; // {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, {GW, 5}, {GW, 6}, {GW, 7}, {GW, 8}, {GW, 9}
uint64_t bw : 10; // {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, {BW, 5}, {BW, 6}, {BW, 7}, {BW, 8}, {BW, 9}
uint64_t rx : 4; // {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}
uint64_t rw15 : 1; // {RW,15}
uint64_t rw14 : 1; // {RW,14}
uint64_t rw13 : 1; // {RW,13}
uint64_t rw12 : 1; // {RW,12}
uint64_t rw11 : 1; // {RW,11}
uint64_t rw10 : 1; // {RW,10}
uint64_t gx : 4; // {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}
uint64_t gw15 : 1; // {GW,15}
uint64_t gw14 : 1; // {GW,14}
uint64_t gw13 : 1; // {GW,13}
uint64_t gw12 : 1; // {GW,12}
uint64_t gw11 : 1; // {GW,11}
uint64_t gw10 : 1; // {GW,10}
uint64_t bx : 4; // {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}
uint64_t bw15 : 1; // {BW,15}
uint64_t bw14 : 1; // {BW,14}
uint64_t bw13 : 1; // {BW,13}
uint64_t bw12 : 1; // {BW,12}
uint64_t bw11 : 1; // {BW,11}
uint64_t bw10 : 1; // {BW,10}
uint64_t indices : 63;
};
static_assert(sizeof(bc6h_mode14) == 16, "Block size must be 16 bytes");
const bool bSigned = (image.format == DXGI_FORMAT_BC6H_SF16) ? true : false;
auto m = reinterpret_cast<const bc6h_mode14*>(sptr);
XMINT3 e0_A(int(m->rw | (m->rw10 << 10) | (m->rw11 << 11) | (m->rw12 << 12) | (m->rw13 << 13) | (m->rw14 << 14) | (m->rw15 << 15)),
int(m->gw | (m->gw10 << 10) | (m->gw11 << 11) | (m->gw12 << 12) | (m->gw13 << 13) | (m->gw14 << 14) | (m->gw15 << 15)),
int(m->bw | (m->bw10 << 10) | (m->bw11 << 11) | (m->bw12 << 12) | (m->bw13 << 13) | (m->bw14 << 14) | (m->bw15 << 15)));
XMINT3 e0_B(int(m->rx), int(m->gx), int(m->bx));
if (bSigned)
{
e0_A.x = SIGN_EXTEND(e0_A.x, 16);
e0_A.y = SIGN_EXTEND(e0_A.y, 16);
e0_A.z = SIGN_EXTEND(e0_A.z, 16);
e0_B.x = SIGN_EXTEND(e0_B.x, 4);
e0_B.y = SIGN_EXTEND(e0_B.y, 4);
e0_B.z = SIGN_EXTEND(e0_B.z, 4);
}
wprintf(L"\tMode 14 - [16 4]\n");
wprintf(L"\t E(A): (%04X, %04X, %04X)\n", e0_A.x & 0xFFFF, e0_A.y & 0xFFFF, e0_A.z & 0xFFFF);
wprintf(L"\t E(B): (%04X, %04X, %04X)\n", e0_B.x & 0xFFFF, e0_B.y & 0xFFFF, e0_B.z & 0xFFFF);
wprintf(L"\t Index: ");
PrintIndex4bpp(m->indices, 0, 0);
wprintf(L"\n");
}
break;
case 0x13: // Reserved mode (5 bits, 10011)
wprintf(L"\tERROR - Reserved mode 10011\n");
break;
case 0x17: // Reserved mode (5 bits, 10111)
wprintf(L"\tERROR - Reserved mode 10011\n");
break;
case 0x1B: // Reserved mode (5 bits, 11011)
wprintf(L"\tERROR - Reserved mode 11011\n");
break;
case 0x1F: // Reserved mode (5 bits, 11111)
wprintf(L"\tERROR - Reserved mode 11111\n");
break;
default:
break;
}
break;
}
break;
case DXGI_FORMAT_BC7_UNORM:
case DXGI_FORMAT_BC7_UNORM_SRGB:
// http://msdn.microsoft.com/en-us/library/windows/desktop/hh308954.aspx
if (*sptr & 0x01)
{
// Mode 0 (1)
struct bc7_mode0
{
uint64_t mode : 1;
uint64_t part : 4;
uint64_t r0 : 4;
uint64_t r1 : 4;
uint64_t r2 : 4;
uint64_t r3 : 4;
uint64_t r4 : 4;
uint64_t r5 : 4;
uint64_t g0 : 4;
uint64_t g1 : 4;
uint64_t g2 : 4;
uint64_t g3 : 4;
uint64_t g4 : 4;
uint64_t g5 : 4;
uint64_t b0 : 4;
uint64_t b1 : 4;
uint64_t b2 : 3;
uint64_t b2n : 1;
uint64_t b3 : 4;
uint64_t b4 : 4;
uint64_t b5 : 4;
uint64_t P0 : 1;
uint64_t P1 : 1;
uint64_t P2 : 1;
uint64_t P3 : 1;
uint64_t P4 : 1;
uint64_t P5 : 1;
uint64_t index : 45;
};
static_assert(sizeof(bc7_mode0) == 16, "Block size must be 16 bytes");
auto m = reinterpret_cast<const bc7_mode0*>(sptr);
wprintf(L"\tMode 0 - [4 4 4] partition %llu\n", m->part);
wprintf(L"\t E0:(%0.3f, %0.3f, %0.3f)\n", float((m->r0 << 1) | m->P0) / 31.f, float((m->g0 << 1) | m->P0) / 31.f, float((m->b0 << 1) | m->P0) / 31.f);
wprintf(L"\t E1:(%0.3f, %0.3f, %0.3f)\n", float((m->r1 << 1) | m->P1) / 31.f, float((m->g1 << 1) | m->P1) / 31.f, float((m->b1 << 1) | m->P1) / 31.f);
wprintf(L"\t E2:(%0.3f, %0.3f, %0.3f)\n", float((m->r2 << 1) | m->P2) / 31.f, float((m->g2 << 1) | m->P2) / 31.f, float(((m->b2 | (m->b2n << 3)) << 1) | m->P2) / 31.f);
wprintf(L"\t E3:(%0.3f, %0.3f, %0.3f)\n", float((m->r3 << 1) | m->P3) / 31.f, float((m->g3 << 1) | m->P3) / 31.f, float((m->b3 << 1) | m->P3) / 31.f);
wprintf(L"\t E4:(%0.3f, %0.3f, %0.3f)\n", float((m->r4 << 1) | m->P4) / 31.f, float((m->g4 << 1) | m->P4) / 31.f, float((m->b4 << 1) | m->P4) / 31.f);
wprintf(L"\t E5:(%0.3f, %0.3f, %0.3f)\n", float((m->r5 << 1) | m->P5) / 31.f, float((m->g5 << 1) | m->P5) / 31.f, float((m->b5 << 1) | m->P5) / 31.f);
wprintf(L"\t Index: ");
PrintIndex2bpp(m->index, 2, m->part);
wprintf(L"\n");
}
else if (*sptr & 0x02)
{
// Mode 1 (01)
struct bc7_mode1
{
uint64_t mode : 2;
uint64_t part : 6;
uint64_t r0 : 6;
uint64_t r1 : 6;
uint64_t r2 : 6;
uint64_t r3 : 6;
uint64_t g0 : 6;
uint64_t g1 : 6;
uint64_t g2 : 6;
uint64_t g3 : 6;
uint64_t b0 : 6;
uint64_t b1 : 2;
uint64_t b1n : 4;
uint64_t b2 : 6;
uint64_t b3 : 6;
uint64_t P0 : 1;
uint64_t P1 : 1;
uint64_t index : 46;
};
static_assert(sizeof(bc7_mode1) == 16, "Block size must be 16 bytes");
auto m = reinterpret_cast<const bc7_mode1*>(sptr);
wprintf(L"\tMode 1 - [6 6 6] partition %llu\n", m->part);
wprintf(L"\t E0:(%0.3f, %0.3f, %0.3f)\n", float((m->r0 << 1) | m->P0) / 127.f, float((m->g0 << 1) | m->P0) / 127.f, float((m->b0 << 1) | m->P0) / 127.f);
wprintf(L"\t E1:(%0.3f, %0.3f, %0.3f)\n", float((m->r1 << 1) | m->P0) / 127.f, float((m->g1 << 1) | m->P0) / 127.f, float(((m->b1 | (m->b1n << 2)) << 1) | m->P0) / 127.f);
wprintf(L"\t E2:(%0.3f, %0.3f, %0.3f)\n", float((m->r2 << 1) | m->P1) / 127.f, float((m->g2 << 1) | m->P1) / 127.f, float((m->b2 << 1) | m->P1) / 127.f);
wprintf(L"\t E3:(%0.3f, %0.3f, %0.3f)\n", float((m->r3 << 1) | m->P1) / 127.f, float((m->g3 << 1) | m->P1) / 127.f, float((m->b3 << 1) | m->P1) / 127.f);
wprintf(L"\t Index: ");
PrintIndex3bpp(m->index, 1, m->part);
wprintf(L"\n");
}
else if (*sptr & 0x04)
{
// Mode 2 (001)
struct bc7_mode2
{
uint64_t mode : 3;
uint64_t part : 6;
uint64_t r0 : 5;
uint64_t r1 : 5;
uint64_t r2 : 5;
uint64_t r3 : 5;
uint64_t r4 : 5;
uint64_t r5 : 5;
uint64_t g0 : 5;
uint64_t g1 : 5;
uint64_t g2 : 5;
uint64_t g3 : 5;
uint64_t g4 : 5;
uint64_t g5 : 5;
uint64_t b0 : 5;
uint64_t b1 : 5;
uint64_t b2 : 5;
uint64_t b3 : 5;
uint64_t b4 : 5;
uint64_t b5 : 5;
uint64_t index : 29;
};
static_assert(sizeof(bc7_mode2) == 16, "Block size must be 16 bytes");
auto m = reinterpret_cast<const bc7_mode2*>(sptr);
wprintf(L"\tMode 2 - [5 5 5] partition %llu\n", m->part);
wprintf(L"\t E0:(%0.3f, %0.3f, %0.3f)\n", float(m->r0) / 31.f, float(m->g0) / 31.f, float(m->b0) / 31.f);
wprintf(L"\t E1:(%0.3f, %0.3f, %0.3f)\n", float(m->r1) / 31.f, float(m->g1) / 31.f, float(m->b1) / 31.f);
wprintf(L"\t E2:(%0.3f, %0.3f, %0.3f)\n", float(m->r2) / 31.f, float(m->g2) / 31.f, float(m->b2) / 31.f);
wprintf(L"\t E3:(%0.3f, %0.3f, %0.3f)\n", float(m->r3) / 31.f, float(m->g3) / 31.f, float(m->b3) / 31.f);
wprintf(L"\t E4:(%0.3f, %0.3f, %0.3f)\n", float(m->r4) / 31.f, float(m->g4) / 31.f, float(m->b4) / 31.f);
wprintf(L"\t E5:(%0.3f, %0.3f, %0.3f)\n", float(m->r5) / 31.f, float(m->g5) / 31.f, float(m->b5) / 31.f);
wprintf(L"\t Index: ");
PrintIndex2bpp(m->index, 2, m->part);
wprintf(L"\n");
}
else if (*sptr & 0x08)
{
// Mode 3 (0001)
struct bc7_mode3
{
uint64_t mode : 4;
uint64_t part : 6;
uint64_t r0 : 7;
uint64_t r1 : 7;
uint64_t r2 : 7;
uint64_t r3 : 7;
uint64_t g0 : 7;
uint64_t g1 : 7;
uint64_t g2 : 7;
uint64_t g3 : 5;
uint64_t g3n : 2;
uint64_t b0 : 7;
uint64_t b1 : 7;
uint64_t b2 : 7;
uint64_t b3 : 7;
uint64_t P0 : 1;
uint64_t P1 : 1;
uint64_t P2 : 1;
uint64_t P3 : 1;
uint64_t index : 30;
};
static_assert(sizeof(bc7_mode3) == 16, "Block size must be 16 bytes");
auto m = reinterpret_cast<const bc7_mode3*>(sptr);
wprintf(L"\tMode 3 - [7 7 7] partition %llu\n", m->part);
wprintf(L"\t E0:(%0.3f, %0.3f, %0.3f)\n", float((m->r0 << 1) | m->P0) / 255.f, float((m->g0 << 1) | m->P0) / 255.f, float((m->b0 << 1) | m->P0) / 255.f);
wprintf(L"\t E1:(%0.3f, %0.3f, %0.3f)\n", float((m->r1 << 1) | m->P1) / 255.f, float((m->g1 << 1) | m->P1) / 255.f, float((m->b1 << 1) | m->P1) / 255.f);
wprintf(L"\t E2:(%0.3f, %0.3f, %0.3f)\n", float((m->r2 << 1) | m->P2) / 255.f, float((m->g2 << 1) | m->P2) / 255.f, float((m->b2 << 1) | m->P2) / 255.f);
wprintf(L"\t E3:(%0.3f, %0.3f, %0.3f)\n", float((m->r3 << 1) | m->P3) / 255.f, float(((m->g3 | (m->g3n << 5)) << 1) | m->P3) / 255.f, float((m->b3 << 1) | m->P3) / 255.f);
wprintf(L"\t Index: ");
PrintIndex2bpp(m->index, 1, m->part);
wprintf(L"\n");
}
else if (*sptr & 0x10)
{
// Mode 4 (00001)
struct bc7_mode4
{
uint64_t mode : 5;
uint64_t rot : 2;
uint64_t idx : 1;
uint64_t r0 : 5;
uint64_t r1 : 5;
uint64_t g0 : 5;
uint64_t g1 : 5;
uint64_t b0 : 5;
uint64_t b1 : 5;
uint64_t a0 : 6;
uint64_t a1 : 6;
uint64_t color_index : 14;
uint64_t color_indexn : 17;
uint64_t alpha_index : 47;
};
static_assert(sizeof(bc7_mode4) == 16, "Block size must be 16 bytes");
auto m = reinterpret_cast<const bc7_mode4*>(sptr);
wprintf(L"\tMode 4 - [5 5 5 A6] indx mode %ls, rot-bits %llu%ls\n", m->idx ? L"3-bit" : L"2-bit", m->rot, GetRotBits(m->rot));
wprintf(L"\t C0:(%0.3f, %0.3f, %0.3f)\n", float(m->r0) / 31.f, float(m->g0) / 31.f, float(m->b0) / 31.f);
wprintf(L"\t C1:(%0.3f, %0.3f, %0.3f)\n", float(m->r1) / 31.f, float(m->g1) / 31.f, float(m->b1) / 31.f);
wprintf(L"\t A0:(%0.3f)\n", float(m->a0) / 63.f);
wprintf(L"\t A1:(%0.3f)\n", float(m->a1) / 63.f);
wprintf(L"\t Colors: ");
const uint64_t color_index = uint64_t(m->color_index) | uint64_t(m->color_indexn << 14);
if (m->idx)
PrintIndex3bpp(color_index, 0, 0);
else
PrintIndex2bpp(color_index, 0, 0);
wprintf(L"\n");
wprintf(L"\t Alpha: ");
PrintIndex3bpp(m->alpha_index, 0, 0);
wprintf(L"\n");
}
else if (*sptr & 0x20)
{
// Mode 5 (000001)
struct bc7_mode5
{
uint64_t mode : 6;
uint64_t rot : 2;
uint64_t r0 : 7;
uint64_t r1 : 7;
uint64_t g0 : 7;
uint64_t g1 : 7;
uint64_t b0 : 7;
uint64_t b1 : 7;
uint64_t a0 : 8;
uint64_t a1 : 6;
uint64_t a1n : 2;
uint64_t color_index : 31;
uint64_t alpha_index : 31;
};
static_assert(sizeof(bc7_mode5) == 16, "Block size must be 16 bytes");
auto m = reinterpret_cast<const bc7_mode5*>(sptr);
wprintf(L"\tMode 5 - [7 7 7 A8] rot-bits %llu%ls\n", m->rot, GetRotBits(m->rot));
wprintf(L"\t C0:(%0.3f, %0.3f, %0.3f)\n", float(m->r0) / 127.f, float(m->g0) / 127.f, float(m->b0) / 127.f);
wprintf(L"\t C1:(%0.3f, %0.3f, %0.3f)\n", float(m->r1) / 127.f, float(m->g1) / 127.f, float(m->b1) / 127.f);
wprintf(L"\t A0:(%0.3f)\n", float(m->a0) / 255.f);
wprintf(L"\t A1:(%0.3f)\n", float(m->a1 | (m->a1n << 6)) / 255.f);
wprintf(L"\t Colors: ");
PrintIndex2bpp(m->color_index, 0, 0);
wprintf(L"\n");
wprintf(L"\t Alpha: ");
PrintIndex2bpp(m->alpha_index, 0, 0);
wprintf(L"\n");
}
else if (*sptr & 0x40)
{
// Mode 6 (0000001)
struct bc7_mode6
{
uint64_t mode : 7;
uint64_t r0 : 7;
uint64_t r1 : 7;
uint64_t g0 : 7;
uint64_t g1 : 7;
uint64_t b0 : 7;
uint64_t b1 : 7;
uint64_t a0 : 7;
uint64_t a1 : 7;
uint64_t P0 : 1;
uint64_t P1 : 1;
uint64_t index : 63;
};
static_assert(sizeof(bc7_mode6) == 16, "Block size must be 16 bytes");
auto m = reinterpret_cast<const bc7_mode6*>(sptr);
wprintf(L"\tMode 6 - [7 7 7 A7]\n");
wprintf(L"\t C0:(%0.3f, %0.3f, %0.3f)\n", float((m->r0 << 1) | m->P0) / 255.f, float((m->g0 << 1) | m->P0) / 255.f, float((m->b0 << 1) | m->P0) / 255.f);
wprintf(L"\t C1:(%0.3f, %0.3f, %0.3f)\n", float((m->r1 << 1) | m->P1) / 255.f, float((m->g1 << 1) | m->P1) / 255.f, float((m->b1 << 1) | m->P1) / 255.f);
wprintf(L"\t A0:(%0.3f)\n", float((m->a0 << 1) | m->P0) / 255.f);
wprintf(L"\t A1:(%0.3f)\n", float((m->a1 << 1) | m->P1) / 255.f);
wprintf(L"\t Index: ");
PrintIndex4bpp(m->index, 0, 0);
wprintf(L"\n");
}
else if (*sptr & 0x80)
{
// Mode 7 (00000001)
struct bc7_mode7
{
uint64_t mode : 8;
uint64_t part : 6;
uint64_t r0 : 5;
uint64_t r1 : 5;
uint64_t r2 : 5;
uint64_t r3 : 5;
uint64_t g0 : 5;
uint64_t g1 : 5;
uint64_t g2 : 5;
uint64_t g3 : 5;
uint64_t b0 : 5;
uint64_t b1 : 5;
uint64_t b2 : 5;
uint64_t b3 : 5;
uint64_t a0 : 5;
uint64_t a1 : 5;
uint64_t a2 : 5;
uint64_t a3 : 5;
uint64_t P0 : 1;
uint64_t P1 : 1;
uint64_t P2 : 1;
uint64_t P3 : 1;
uint64_t index : 30;
};
static_assert(sizeof(bc7_mode7) == 16, "Block size must be 16 bytes");
auto m = reinterpret_cast<const bc7_mode7*>(sptr);
wprintf(L"\tMode 7 - [5 5 5 A5] partition %llu\n", m->part);
wprintf(L"\t C0:(%0.3f, %0.3f, %0.3f)\n", float((m->r0 << 1) | m->P0) / 63.f, float((m->g0 << 1) | m->P0) / 63.f, float((m->b0 << 1) | m->P0) / 63.f);
wprintf(L"\t C1:(%0.3f, %0.3f, %0.3f)\n", float((m->r1 << 1) | m->P1) / 63.f, float((m->g1 << 1) | m->P1) / 63.f, float((m->b1 << 1) | m->P1) / 63.f);
wprintf(L"\t C2:(%0.3f, %0.3f, %0.3f)\n", float((m->r2 << 1) | m->P2) / 63.f, float((m->g2 << 1) | m->P2) / 63.f, float((m->b2 << 1) | m->P2) / 63.f);
wprintf(L"\t C3:(%0.3f, %0.3f, %0.3f)\n", float((m->r3 << 1) | m->P3) / 63.f, float((m->g3 << 1) | m->P3) / 63.f, float((m->b3 << 1) | m->P3) / 63.f);
wprintf(L"\t A0:(%0.3f)\n", float((m->a0 << 1) | m->P0) / 63.f);
wprintf(L"\t A1:(%0.3f)\n", float((m->a1 << 1) | m->P1) / 63.f);
wprintf(L"\t A2:(%0.3f)\n", float((m->a2 << 1) | m->P2) / 63.f);
wprintf(L"\t A3:(%0.3f)\n", float((m->a3 << 1) | m->P3) / 63.f);
wprintf(L"\t Index: ");
PrintIndex4bpp(m->index, 1, m->part);
wprintf(L"\n");
}
else
{
// Reserved mode 8 (00000000)
wprintf(L"\tERROR - Reserved mode 8\n");
}
break;
}
}
}
return S_OK;
}
#pragma endregion
}
//--------------------------------------------------------------------------------------
// Entry-point
//--------------------------------------------------------------------------------------
#ifdef _PREFAST_
#pragma prefast(disable : 28198, "Command-line tool, frees all memory on exit")
#endif
int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
{
// Parameters and defaults
TEX_FILTER_FLAGS dwFilter = TEX_FILTER_DEFAULT;
int pixelx = -1;
int pixely = -1;
uint32_t diffColor = 0;
float threshold = 0.25f;
DXGI_FORMAT diffFormat = DXGI_FORMAT_B8G8R8A8_UNORM;
uint32_t fileType = WIC_CODEC_BMP;
std::wstring outputFile;
// Set locale for output since GetErrorDesc can get localized strings.
std::locale::global(std::locale(""));
// Initialize COM (needed for WIC)
HRESULT hr = hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
if (FAILED(hr))
{
wprintf(L"Failed to initialize COM (%08X%ls)\n", static_cast<unsigned int>(hr), GetErrorDesc(hr));
return 1;
}
// Process command line
if (argc < 2)
{
PrintUsage();
return 0;
}
if (('-' == argv[1][0]) && ('-' == argv[1][1]))
{
if (!_wcsicmp(argv[1], L"--version"))
{
PrintLogo(true, g_ToolName, g_Description);
return 0;
}
else if (!_wcsicmp(argv[1], L"--help"))
{
PrintUsage();
return 0;
}
}
const uint32_t dwCommand = LookupByName(argv[1], g_pCommands);
switch (dwCommand)
{
case CMD_INFO:
case CMD_ANALYZE:
case CMD_COMPARE:
case CMD_DIFF:
case CMD_DUMPBC:
case CMD_DUMPDDS:
break;
default:
wprintf(L"Must use one of: info, analyze, compare, diff, dumpbc, or dumpdds\n\n");
return 1;
}
uint32_t dwOptions = 0;
std::list<SConversion> conversion;
bool allowOpts = true;
for (int iArg = 2; iArg < argc; iArg++)
{
PWSTR pArg = argv[iArg];
if (allowOpts
&& ('-' == pArg[0]) && ('-' == pArg[1]))
{
if (pArg[2] == 0)
{
// "-- " is the POSIX standard for "end of options" marking to escape the '-' and '/' characters at the start of filepaths.
allowOpts = false;
}
else if (!_wcsicmp(pArg, L"--version"))
{
PrintLogo(true, g_ToolName, g_Description);
return 0;
}
else if (!_wcsicmp(pArg, L"--help"))
{
PrintUsage();
return 0;
}
else
{
wprintf(L"Unknown option: %ls\n", pArg);
return 1;
}
}
else if (allowOpts
&& (('-' == pArg[0]) || ('/' == pArg[0])))
{
pArg++;
PWSTR pValue;
for (pValue = pArg; *pValue && (':' != *pValue); pValue++);
if (*pValue)
*pValue++ = 0;
const uint32_t dwOption = LookupByName(pArg, g_pOptions);
if (!dwOption || (dwOptions & (1 << dwOption)))
{
PrintUsage();
return 1;
}
dwOptions |= 1 << dwOption;
// Handle options with additional value parameter
switch (dwOption)
{
case OPT_FILTER:
case OPT_FORMAT:
case OPT_FILETYPE:
case OPT_OUTPUTFILE:
case OPT_TARGET_PIXELX:
case OPT_TARGET_PIXELY:
case OPT_DIFF_COLOR:
case OPT_THRESHOLD:
case OPT_FILELIST:
if (!*pValue)
{
if ((iArg + 1 >= argc))
{
PrintUsage();
return 1;
}
iArg++;
pValue = argv[iArg];
}
break;
default:
break;
}
switch (dwOption)
{
case OPT_FORMAT:
if (dwCommand != CMD_DIFF)
{
wprintf(L"-f only valid for use with diff command\n");
return 1;
}
else
{
diffFormat = static_cast<DXGI_FORMAT>(LookupByName(pValue, g_pFormats));
if (!diffFormat)
{
diffFormat = static_cast<DXGI_FORMAT>(LookupByName(pValue, g_pFormatAliases));
if (!diffFormat)
{
wprintf(L"Invalid value specified with -f (%ls)\n", pValue);
return 1;
}
}
}
break;
case OPT_FILTER:
dwFilter = static_cast<TEX_FILTER_FLAGS>(LookupByName(pValue, g_pFilters));
if (!dwFilter)
{
wprintf(L"Invalid value specified with -if (%ls)\n", pValue);
return 1;
}
break;
case OPT_OUTPUTFILE:
if (dwCommand != CMD_DIFF)
{
wprintf(L"-o only valid for use with diff command\n");
return 1;
}
else
{
std::filesystem::path path(pValue);
outputFile = path.make_preferred().native();
fileType = LookupByName(path.extension().c_str(), g_pExtFileTypes);
}
break;
case OPT_FILETYPE:
if (dwCommand != CMD_DUMPDDS)
{
wprintf(L"-ft only valid for use with dumpdds command\n");
return 1;
}
else
{
fileType = LookupByName(pValue, g_pDumpFileTypes);
if (!fileType)
{
wprintf(L"Invalid value specified with -ft (%ls)\n", pValue);
wprintf(L"\n");
PrintUsage();
return 1;
}
}
break;
case OPT_TARGET_PIXELX:
if (dwCommand != CMD_DUMPBC)
{
wprintf(L"-targetx only valid with dumpbc command\n");
return 1;
}
else if (swscanf_s(pValue, L"%d", &pixelx) != 1)
{
wprintf(L"Invalid value for pixel x location (%ls)\n", pValue);
return 1;
}
break;
case OPT_TARGET_PIXELY:
if (dwCommand != CMD_DUMPBC)
{
wprintf(L"-targety only valid with dumpbc command\n");
return 1;
}
else if (swscanf_s(pValue, L"%d", &pixely) != 1)
{
wprintf(L"Invalid value for pixel y location (%ls)\n", pValue);
return 1;
}
break;
case OPT_DIFF_COLOR:
if (swscanf_s(pValue, L"%x", &diffColor) != 1)
{
printf("Invalid value specified with -c (%ls)\n", pValue);
printf("\n");
PrintUsage();
return 1;
}
diffColor &= 0xFFFFFF;
break;
case OPT_THRESHOLD:
if (swscanf_s(pValue, L"%f", &threshold) != 1)
{
printf("Invalid value specified with -t (%ls)\n", pValue);
printf("\n");
PrintUsage();
return 1;
}
break;
case OPT_FILELIST:
{
std::filesystem::path path(pValue);
std::wifstream inFile(path.make_preferred().c_str());
if (!inFile)
{
wprintf(L"Error opening -flist file %ls\n", pValue);
return 1;
}
inFile.imbue(std::locale::classic());
ProcessFileList(inFile, conversion);
}
break;
default:
break;
}
}
else if (wcspbrk(pArg, L"?*") != nullptr)
{
const size_t count = conversion.size();
std::filesystem::path path(pArg);
SearchForFiles(path.make_preferred(), conversion, (dwOptions & (1 << OPT_RECURSIVE)) != 0, nullptr);
if (conversion.size() <= count)
{
wprintf(L"No matching files found for %ls\n", pArg);
return 1;
}
}
else
{
SConversion conv = {};
std::filesystem::path path(pArg);
conv.szSrc = path.make_preferred().native();
conversion.push_back(conv);
}
}
if (conversion.empty())
{
PrintUsage();
return 0;
}
if (~dwOptions & (1 << OPT_NOLOGO))
PrintLogo(false, g_ToolName, g_Description);
switch (dwCommand)
{
case CMD_COMPARE:
case CMD_DIFF:
// --- Compare/Diff ------------------------------------------------------------
if (conversion.size() != 2)
{
wprintf(L"ERROR: compare/diff needs exactly two images\n");
return 1;
}
else
{
auto pImage1 = conversion.cbegin();
wprintf(L"1: %ls", pImage1->szSrc.c_str());
fflush(stdout);
TexMetadata info1;
std::unique_ptr<ScratchImage> image1;
hr = LoadImage(pImage1->szSrc.c_str(), dwOptions, dwFilter, info1, image1);
if (FAILED(hr))
{
wprintf(L" FAILED (%08X%ls)\n", static_cast<unsigned int>(hr), GetErrorDesc(hr));
return 1;
}
auto pImage2 = conversion.cbegin();
std::advance(pImage2, 1);
wprintf(L"\n2: %ls", pImage2->szSrc.c_str());
fflush(stdout);
TexMetadata info2;
std::unique_ptr<ScratchImage> image2;
hr = LoadImage(pImage2->szSrc.c_str(), dwOptions, dwFilter, info2, image2);
if (FAILED(hr))
{
wprintf(L" FAILED (%08X%ls)\n", static_cast<unsigned int>(hr), GetErrorDesc(hr));
return 1;
}
wprintf(L"\n");
fflush(stdout);
if (info1.height != info2.height
|| info1.width != info2.width)
{
wprintf(L"ERROR: Can only compare/diff images of the same width & height\n");
return 1;
}
if (dwCommand == CMD_DIFF)
{
if (outputFile.empty())
{
std::filesystem::path curpath(pImage1->szSrc);
auto const ext = curpath.extension();
if (_wcsicmp(ext.c_str(), L".bmp") == 0)
{
wprintf(L"ERROR: Need to specify output file via -o\n");
return 1;
}
outputFile = curpath.stem().concat(L".bmp").native();
}
if (image1->GetImageCount() > 1 || image2->GetImageCount() > 1)
wprintf(L"WARNING: ignoring all images but first one in each file\n");
ScratchImage diffImage;
hr = Difference(*image1->GetImage(0, 0, 0), *image2->GetImage(0, 0, 0), dwFilter, diffFormat, diffColor, threshold, diffImage);
if (FAILED(hr))
{
wprintf(L"Failed diffing images (%08X%ls)\n", static_cast<unsigned int>(hr), GetErrorDesc(hr));
return 1;
}
if (dwOptions & (1 << OPT_TOLOWER))
{
std::transform(outputFile.begin(), outputFile.end(), outputFile.begin(), towlower);
}
if (~dwOptions & (1 << OPT_OVERWRITE))
{
if (GetFileAttributesW(outputFile.c_str()) != INVALID_FILE_ATTRIBUTES)
{
wprintf(L"\nERROR: Output file already exists, use -y to overwrite\n");
return 1;
}
}
hr = SaveImage(diffImage.GetImage(0, 0, 0), outputFile.c_str(), fileType);
if (FAILED(hr))
{
wprintf(L" FAILED (%08X%ls)\n", static_cast<unsigned int>(hr), GetErrorDesc(hr));
return 1;
}
wprintf(L"Difference %ls\n", outputFile.c_str());
}
else if ((info1.depth == 1
&& info1.arraySize == 1
&& info1.mipLevels == 1)
|| info1.depth != info2.depth
|| info1.arraySize != info2.arraySize
|| info1.mipLevels != info2.mipLevels
|| image1->GetImageCount() != image2->GetImageCount())
{
// Compare single image
if (image1->GetImageCount() > 1 || image2->GetImageCount() > 1)
wprintf(L"WARNING: ignoring all images but first one in each file\n");
float mse, mseV[4];
hr = ComputeMSE(*image1->GetImage(0, 0, 0), *image2->GetImage(0, 0, 0), mse, mseV);
if (FAILED(hr))
{
wprintf(L"Failed comparing images (%08X%ls)\n", static_cast<unsigned int>(hr), GetErrorDesc(hr));
return 1;
}
wprintf(L"Result: %f (%f %f %f %f) PSNR %f dB\n", mse, mseV[0], mseV[1], mseV[2], mseV[3],
10.0 * log10(3.0 / (double(mseV[0]) + double(mseV[1]) + double(mseV[2]))));
}
else
{
// Compare all images
float min_mse = FLT_MAX;
float min_mseV[4] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX };
float max_mse = -FLT_MAX;
float max_mseV[4] = { -FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX };
double sum_mse = 0;
double sum_mseV[4] = { 0, 0, 0, 0 };
size_t total_images = 0;
if (info1.depth > 1)
{
wprintf(L"Results by mip (%3zu) and slice (%3zu)\n\n", info1.mipLevels, info1.depth);
size_t depth = info1.depth;
for (size_t mip = 0; mip < info1.mipLevels; ++mip)
{
for (size_t slice = 0; slice < depth; ++slice)
{
const Image* img1 = image1->GetImage(mip, 0, slice);
const Image* img2 = image2->GetImage(mip, 0, slice);
if (!img1
|| !img2
|| img1->height != img2->height
|| img1->width != img2->width)
{
wprintf(L"ERROR: Unexpected mismatch at slice %3zu, mip %3zu\n", slice, mip);
return 1;
}
else
{
float mse, mseV[4];
hr = ComputeMSE(*img1, *img2, mse, mseV);
if (FAILED(hr))
{
wprintf(L"Failed comparing images at slice %3zu, mip %3zu (%08X%ls)\n", slice, mip, static_cast<unsigned int>(hr), GetErrorDesc(hr));
return 1;
}
min_mse = std::min(min_mse, mse);
max_mse = std::max(max_mse, mse);
sum_mse += double(mse);
for (size_t j = 0; j < 4; ++j)
{
min_mseV[j] = std::min(min_mseV[j], mseV[j]);
max_mseV[j] = std::max(max_mseV[j], mseV[j]);
sum_mseV[j] += double(mseV[j]);
}
++total_images;
wprintf(L"[%3zu,%3zu]: %f (%f %f %f %f) PSNR %f dB\n", mip, slice, mse, mseV[0], mseV[1], mseV[2], mseV[3],
10.0 * log10(3.0 / (double(mseV[0]) + double(mseV[1]) + double(mseV[2]))));
}
}
if (depth > 1)
depth >>= 1;
}
}
else
{
wprintf(L"Results by item (%3zu) and mip (%3zu)\n\n", info1.arraySize, info1.mipLevels);
for (size_t item = 0; item < info1.arraySize; ++item)
{
for (size_t mip = 0; mip < info1.mipLevels; ++mip)
{
const Image* img1 = image1->GetImage(mip, item, 0);
const Image* img2 = image2->GetImage(mip, item, 0);
if (!img1
|| !img2
|| img1->height != img2->height
|| img1->width != img2->width)
{
wprintf(L"ERROR: Unexpected mismatch at item %3zu, mip %3zu\n", item, mip);
return 1;
}
else
{
float mse, mseV[4];
hr = ComputeMSE(*img1, *img2, mse, mseV);
if (FAILED(hr))
{
wprintf(L"Failed comparing images at item %3zu, mip %3zu (%08X%ls)\n", item, mip, static_cast<unsigned int>(hr), GetErrorDesc(hr));
return 1;
}
min_mse = std::min(min_mse, mse);
max_mse = std::max(max_mse, mse);
sum_mse += double(mse);
for (size_t j = 0; j < 4; ++j)
{
min_mseV[j] = std::min(min_mseV[j], mseV[j]);
max_mseV[j] = std::max(max_mseV[j], mseV[j]);
sum_mseV[j] += double(mseV[j]);
}
++total_images;
wprintf(L"[%3zu,%3zu]: %f (%f %f %f %f) PSNR %f dB\n", item, mip, mse, mseV[0], mseV[1], mseV[2], mseV[3],
10.0 * log10(3.0 / (double(mseV[0]) + double(mseV[1]) + double(mseV[2]))));
}
}
}
}
// Output multi-image stats
if (total_images > 1)
{
wprintf(L"\n Minimum MSE: %f (%f %f %f %f) PSNR %f dB\n", min_mse, min_mseV[0], min_mseV[1], min_mseV[2], min_mseV[3],
10.0 * log10(3.0 / (double(min_mseV[0]) + double(min_mseV[1]) + double(min_mseV[2]))));
const double total_mseV0 = sum_mseV[0] / double(total_images);
const double total_mseV1 = sum_mseV[1] / double(total_images);
const double total_mseV2 = sum_mseV[2] / double(total_images);
wprintf(L" Average MSE: %f (%f %f %f %f) PSNR %f dB\n", sum_mse / double(total_images),
total_mseV0,
total_mseV1,
total_mseV2,
sum_mseV[3] / double(total_images),
10.0 * log10(3.0 / (total_mseV0 + total_mseV1 + total_mseV2)));
wprintf(L" Maximum MSE: %f (%f %f %f %f) PSNR %f dB\n", max_mse, max_mseV[0], max_mseV[1], max_mseV[2], max_mseV[3],
10.0 * log10(3.0 / (double(max_mseV[0]) + double(max_mseV[1]) + double(max_mseV[2]))));
}
}
}
break;
default:
for (auto pConv = conversion.cbegin(); pConv != conversion.cend(); ++pConv)
{
std::filesystem::path curpath(pConv->szSrc);
// Load source image
if (pConv != conversion.begin())
wprintf(L"\n");
wprintf(L"%ls", curpath.c_str());
fflush(stdout);
TexMetadata info;
std::unique_ptr<ScratchImage> image;
hr = LoadImage(curpath.c_str(), dwOptions, dwFilter, info, image);
if (FAILED(hr))
{
wprintf(L" FAILED (%08X%ls)\n", static_cast<unsigned int>(hr), GetErrorDesc(hr));
return 1;
}
wprintf(L"\n");
fflush(stdout);
if (dwCommand == CMD_INFO)
{
// --- Info ----------------------------------------------------------------
wprintf(L" width = %zu\n", info.width);
wprintf(L" height = %zu\n", info.height);
wprintf(L" depth = %zu\n", info.depth);
wprintf(L" mipLevels = %zu\n", info.mipLevels);
wprintf(L" arraySize = %zu\n", info.arraySize);
wprintf(L" format = ");
PrintFormat(info.format, g_pFormats, g_pReadOnlyFormats);
wprintf(L"\n dimension = ");
switch (info.dimension)
{
case TEX_DIMENSION_TEXTURE1D:
wprintf(L"%ls", (info.arraySize > 1) ? L"1DArray\n" : L"1D\n");
break;
case TEX_DIMENSION_TEXTURE2D:
if (info.IsCubemap())
{
wprintf(L"%ls", (info.arraySize > 6) ? L"CubeArray\n" : L"Cube\n");
}
else
{
wprintf(L"%ls", (info.arraySize > 1) ? L"2DArray\n" : L"2D\n");
}
break;
case TEX_DIMENSION_TEXTURE3D:
wprintf(L" 3D");
break;
}
wprintf(L" alpha mode = ");
switch (info.GetAlphaMode())
{
case TEX_ALPHA_MODE_OPAQUE:
wprintf(L"Opaque");
break;
case TEX_ALPHA_MODE_PREMULTIPLIED:
wprintf(L"Premultiplied");
break;
case TEX_ALPHA_MODE_STRAIGHT:
wprintf(L"Straight");
break;
case TEX_ALPHA_MODE_CUSTOM:
wprintf(L"Custom");
break;
case TEX_ALPHA_MODE_UNKNOWN:
wprintf(L"Unknown");
break;
}
wprintf(L"\n images = %zu\n", image->GetImageCount());
auto const sizeInKb = static_cast<uint32_t>(image->GetPixelsSize() / 1024);
wprintf(L" pixel size = %u (KB)\n\n", sizeInKb);
}
else if (dwCommand == CMD_DUMPDDS)
{
// --- Dump DDS ------------------------------------------------------------
if (IsCompressed(info.format))
{
wprintf(L"ERROR: dumpdds only operates on non-compressed format DDS files\n");
return 1;
}
auto const ext = LookupByValue(fileType, g_pDumpFileTypes);
if (info.depth > 1)
{
wprintf(L"Writing by mip (%3zu) and slice (%3zu)...", info.mipLevels, info.depth);
size_t depth = info.depth;
for (size_t mip = 0; mip < info.mipLevels; ++mip)
{
for (size_t slice = 0; slice < depth; ++slice)
{
const Image* img = image->GetImage(mip, 0, slice);
if (!img)
{
wprintf(L"ERROR: Unexpected error at slice %3zu, mip %3zu\n", slice, mip);
return 1;
}
else
{
wchar_t subFname[_MAX_PATH] = {};
if (info.mipLevels > 1)
{
swprintf_s(subFname, L"%ls_slice%03zu_mip%03zu", curpath.stem().c_str(), slice, mip);
}
else
{
swprintf_s(subFname, L"%ls_slice%03zu", curpath.stem().c_str(), slice);
}
outputFile.assign(subFname);
outputFile.append(ext);
hr = SaveImage(img, outputFile.c_str(), fileType);
if (FAILED(hr))
{
wprintf(L" FAILED (%08X%ls)\n", static_cast<unsigned int>(hr), GetErrorDesc(hr));
return 1;
}
}
}
if (depth > 1)
depth >>= 1;
}
wprintf(L"\n");
}
else
{
wprintf(L"Writing by item (%3zu) and mip (%3zu)...", info.arraySize, info.mipLevels);
for (size_t item = 0; item < info.arraySize; ++item)
{
for (size_t mip = 0; mip < info.mipLevels; ++mip)
{
const Image* img = image->GetImage(mip, item, 0);
if (!img)
{
wprintf(L"ERROR: Unexpected error at item %3zu, mip %3zu\n", item, mip);
return 1;
}
else
{
wchar_t subFname[_MAX_PATH] = {};
if (info.mipLevels > 1)
{
swprintf_s(subFname, L"%ls_item%03zu_mip%03zu", curpath.stem().c_str(), item, mip);
}
else
{
swprintf_s(subFname, L"%ls_item%03zu", curpath.stem().c_str(), item);
}
outputFile.assign(subFname);
outputFile.append(ext);
hr = SaveImage(img, outputFile.c_str(), fileType);
if (FAILED(hr))
{
wprintf(L" FAILED (%08X%ls)\n", static_cast<unsigned int>(hr), GetErrorDesc(hr));
return 1;
}
}
}
}
wprintf(L"\n");
}
}
else if (dwCommand == CMD_DUMPBC)
{
// --- Dump BC -------------------------------------------------------------
if (!IsCompressed(info.format))
{
wprintf(L"ERROR: dumpbc only operates on BC format DDS files\n");
return 1;
}
if (pixelx >= int(info.width)
|| pixely >= int(info.height))
{
wprintf(L"WARNING: Specified pixel location (%d x %d) is out of range for image (%zu x %zu)\n", pixelx, pixely, info.width, info.height);
continue;
}
wprintf(L"Compression: ");
PrintFormat(info.format, g_pFormats, g_pReadOnlyFormats);
wprintf(L"\n");
if (info.depth > 1)
{
wprintf(L"Results by mip (%3zu) and slice (%3zu)\n", info.mipLevels, info.depth);
size_t depth = info.depth;
for (size_t mip = 0; mip < info.mipLevels; ++mip)
{
for (size_t slice = 0; slice < depth; ++slice)
{
const Image* img = image->GetImage(mip, 0, slice);
if (!img)
{
wprintf(L"ERROR: Unexpected error at slice %3zu, mip %3zu\n", slice, mip);
return 1;
}
else
{
wprintf(L"\n[%3zu, %3zu]:\n", mip, slice);
hr = DumpBCImage(*img, pixelx, pixely);
if (FAILED(hr))
{
wprintf(L"ERROR: Failed dumping image at slice %3zu, mip %3zu (%08X%ls)\n", slice, mip, static_cast<unsigned int>(hr), GetErrorDesc(hr));
return 1;
}
}
}
if (depth > 1)
depth >>= 1;
if (pixelx > 0)
pixelx >>= 1;
if (pixely > 0)
pixely >>= 1;
}
}
else
{
wprintf(L"Results by item (%3zu) and mip (%3zu)\n", info.arraySize, info.mipLevels);
for (size_t item = 0; item < info.arraySize; ++item)
{
int tpixelx = pixelx;
int tpixely = pixely;
for (size_t mip = 0; mip < info.mipLevels; ++mip)
{
const Image* img = image->GetImage(mip, item, 0);
if (!img)
{
wprintf(L"ERROR: Unexpected error at item %3zu, mip %3zu\n", item, mip);
return 1;
}
else
{
if (image->GetImageCount() > 1)
{
wprintf(L"\n[%3zu, %3zu]:\n", item, mip);
}
hr = DumpBCImage(*img, tpixelx, tpixely);
if (FAILED(hr))
{
wprintf(L"ERROR: Failed dumping image at item %3zu, mip %3zu (%08X%ls)\n", item, mip, static_cast<unsigned int>(hr), GetErrorDesc(hr));
return 1;
}
}
if (tpixelx > 0)
tpixelx >>= 1;
if (tpixely > 0)
tpixely >>= 1;
}
}
}
}
else
{
// --- Analyze -------------------------------------------------------------
if (IsPlanar(info.format))
{
auto img = image->GetImage(0, 0, 0);
assert(img);
const size_t nimg = image->GetImageCount();
std::unique_ptr<ScratchImage> timage(new (std::nothrow) ScratchImage);
if (!timage)
{
wprintf(L"\nERROR: Memory allocation failed\n");
return 1;
}
hr = ConvertToSinglePlane(img, nimg, info, *timage);
if (FAILED(hr))
{
wprintf(L" FAILED [converttosingleplane] (%08X%ls)\n", static_cast<unsigned int>(hr), GetErrorDesc(hr));
return 1;
}
auto& tinfo = timage->GetMetadata();
info.format = tinfo.format;
assert(info.width == tinfo.width);
assert(info.height == tinfo.height);
assert(info.depth == tinfo.depth);
assert(info.arraySize == tinfo.arraySize);
assert(info.mipLevels == tinfo.mipLevels);
assert(info.miscFlags == tinfo.miscFlags);
assert(info.dimension == tinfo.dimension);
image.swap(timage);
}
if (info.depth > 1)
{
wprintf(L"Results by mip (%3zu) and slice (%3zu)\n\n", info.mipLevels, info.depth);
size_t depth = info.depth;
for (size_t mip = 0; mip < info.mipLevels; ++mip)
{
for (size_t slice = 0; slice < depth; ++slice)
{
const Image* img = image->GetImage(mip, 0, slice);
if (!img)
{
wprintf(L"ERROR: Unexpected error at slice %3zu, mip %3zu\n", slice, mip);
return 1;
}
else
{
AnalyzeData data;
hr = Analyze(*img, data);
if (FAILED(hr))
{
wprintf(L"ERROR: Failed analyzing image at slice %3zu, mip %3zu (%08X%ls)\n", slice, mip, static_cast<unsigned int>(hr), GetErrorDesc(hr));
return 1;
}
wprintf(L"Result slice %3zu, mip %3zu:\n", slice, mip);
data.Print();
}
if (IsCompressed(info.format))
{
AnalyzeBCData data;
hr = AnalyzeBC(*img, data);
if (FAILED(hr))
{
wprintf(L"ERROR: Failed analyzing BC image at slice %3zu, mip %3zu (%08X%ls)\n", slice, mip, static_cast<unsigned int>(hr), GetErrorDesc(hr));
return 1;
}
data.Print(img->format);
}
wprintf(L"\n");
}
if (depth > 1)
depth >>= 1;
}
}
else
{
wprintf(L"Results by item (%3zu) and mip (%3zu)\n\n", info.arraySize, info.mipLevels);
for (size_t item = 0; item < info.arraySize; ++item)
{
for (size_t mip = 0; mip < info.mipLevels; ++mip)
{
const Image* img = image->GetImage(mip, item, 0);
if (!img)
{
wprintf(L"ERROR: Unexpected error at item %3zu, mip %3zu\n", item, mip);
return 1;
}
else
{
AnalyzeData data;
hr = Analyze(*img, data);
if (FAILED(hr))
{
wprintf(L"ERROR: Failed analyzing image at item %3zu, mip %3zu (%08X%ls)\n", item, mip, static_cast<unsigned int>(hr), GetErrorDesc(hr));
return 1;
}
if (image->GetImageCount() > 1)
{
wprintf(L"Result item %3zu, mip %3zu:\n", item, mip);
}
data.Print();
}
if (IsCompressed(info.format))
{
AnalyzeBCData data;
hr = AnalyzeBC(*img, data);
if (FAILED(hr))
{
wprintf(L"ERROR: Failed analyzing BC image at item %3zu, mip %3zu (%08X%ls)\n", item, mip, static_cast<unsigned int>(hr), GetErrorDesc(hr));
return 1;
}
data.Print(img->format);
}
wprintf(L"\n");
}
}
}
}
}
break;
}
return 0;
}