//-------------------------------------------------------------------------------------- // 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef _MSC_VER #pragma warning(disable : 4619 4616 26812) #endif #include "DirectXTex.h" #include //Uncomment to add support for OpenEXR (.exr) //#define USE_OPENEXR #ifdef USE_OPENEXR // See for details #include "DirectXTexEXR.h" #endif using namespace DirectX; 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_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"); struct SConversion { std::wstring szSrc; }; struct SValue { const wchar_t* name; uint32_t value; }; ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// const SValue 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 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"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 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 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 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 } }; const SValue 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 const SValue g_pDumpFileTypes[] = { { L"bmp", WIC_CODEC_BMP }, { L"jpg", WIC_CODEC_JPEG }, { L"jpeg", WIC_CODEC_JPEG }, { L"png", WIC_CODEC_PNG }, { 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 g_pExtFileTypes[] = { { L".bmp", WIC_CODEC_BMP }, { L".jpg", WIC_CODEC_JPEG }, { L".jpeg", WIC_CODEC_JPEG }, { L".png", WIC_CODEC_PNG }, { 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 } }; ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// namespace { inline HANDLE safe_handle(HANDLE h) noexcept { return (h == INVALID_HANDLE_VALUE) ? nullptr : h; } struct find_closer { void operator()(HANDLE h) noexcept { assert(h != INVALID_HANDLE_VALUE); if (h) FindClose(h); } }; using ScopedFindHandle = std::unique_ptr; #ifdef _PREFAST_ #pragma prefast(disable : 26018, "Only used with static internal arrays") #endif uint32_t LookupByName(const wchar_t *pName, const SValue *pArray) { while (pArray->name) { if (!_wcsicmp(pName, pArray->name)) return pArray->value; pArray++; } return 0; } const wchar_t* LookupByValue(uint32_t pValue, const SValue *pArray) { while (pArray->name) { if (pValue == pArray->value) return pArray->name; pArray++; } return L""; } void SearchForFiles(const std::filesystem::path& path, std::list& files, bool recursive) { // Process files WIN32_FIND_DATAW findData = {}; ScopedFindHandle hFile(safe_handle(FindFirstFileExW(path.c_str(), FindExInfoBasic, &findData, FindExSearchNameMatch, nullptr, FIND_FIRST_EX_LARGE_FETCH))); if (hFile) { for (;;) { if (!(findData.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY))) { SConversion conv = {}; conv.szSrc = path.parent_path().append(findData.cFileName).native(); files.push_back(conv); } if (!FindNextFileW(hFile.get(), &findData)) break; } } // Process directories if (recursive) { auto searchDir = path.parent_path().append(L"*"); hFile.reset(safe_handle(FindFirstFileExW(searchDir.c_str(), FindExInfoBasic, &findData, FindExSearchLimitToDirectories, nullptr, FIND_FIRST_EX_LARGE_FETCH))); if (!hFile) return; for (;;) { if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if (findData.cFileName[0] != L'.') { auto subdir = path.parent_path().append(findData.cFileName).append(path.filename().c_str()); SearchForFiles(subdir, files, recursive); } } if (!FindNextFileW(hFile.get(), &findData)) break; } } } void ProcessFileList(std::wifstream& inFile, std::list& files) { std::list flist; std::set excludes; for (;;) { std::wstring fname; std::getline(inFile, fname); if (!inFile) break; if (fname[0] == L'#') { // Comment } else if (fname[0] == L'-') { if (flist.empty()) { wprintf(L"WARNING: Ignoring the line '%ls' in -flist\n", fname.c_str()); } else { std::filesystem::path path(fname.c_str() + 1); auto& npath = path.make_preferred(); if (wcspbrk(fname.c_str(), L"?*") != nullptr) { std::list removeFiles; SearchForFiles(npath, removeFiles, false); for (auto& it : removeFiles) { std::wstring name = it.szSrc; std::transform(name.begin(), name.end(), name.begin(), towlower); excludes.insert(name); } } else { std::wstring name = npath.c_str(); std::transform(name.begin(), name.end(), name.begin(), towlower); excludes.insert(name); } } } else if (wcspbrk(fname.c_str(), L"?*") != nullptr) { std::filesystem::path path(fname.c_str()); SearchForFiles(path.make_preferred(), flist, false); } else { SConversion conv = {}; std::filesystem::path path(fname.c_str()); conv.szSrc = path.make_preferred().native(); flist.push_back(conv); } } inFile.close(); if (!excludes.empty()) { // Remove any excluded files for (auto it = flist.begin(); it != flist.end();) { std::wstring name = it->szSrc; std::transform(name.begin(), name.end(), name.begin(), towlower); auto item = it; ++it; if (excludes.find(name) != excludes.end()) { flist.erase(item); } } } if (flist.empty()) { wprintf(L"WARNING: No file names found in -flist\n"); } else { files.splice(files.end(), flist); } } void PrintFormat(DXGI_FORMAT Format) { for (auto pFormat = g_pFormats; pFormat->name; pFormat++) { if (static_cast(pFormat->value) == Format) { wprintf(L"%ls", pFormat->name); return; } } for (auto pFormat = g_pReadOnlyFormats; pFormat->name; pFormat++) { if (static_cast(pFormat->value) == Format) { wprintf(L"%ls", pFormat->name); return; } } wprintf(L"*UNKNOWN*"); } void PrintList(size_t cch, const SValue *pValue) { while (pValue->name) { const size_t cchName = wcslen(pValue->name); if (cch + cchName + 2 >= 80) { wprintf(L"\n "); cch = 6; } wprintf(L"%ls ", pValue->name); cch += cchName + 2; pValue++; } wprintf(L"\n"); } void PrintLogo(bool versionOnly) { wchar_t version[32] = {}; wchar_t appName[_MAX_PATH] = {}; if (GetModuleFileNameW(nullptr, appName, _MAX_PATH)) { const DWORD size = GetFileVersionInfoSizeW(appName, nullptr); if (size > 0) { auto verInfo = std::make_unique(size); if (GetFileVersionInfoW(appName, 0, size, verInfo.get())) { LPVOID lpstr = nullptr; UINT strLen = 0; if (VerQueryValueW(verInfo.get(), L"\\StringFileInfo\\040904B0\\ProductVersion", &lpstr, &strLen)) { wcsncpy_s(version, reinterpret_cast(lpstr), strLen); } } } } if (!*version || wcscmp(version, L"1.0.0.0") == 0) { swprintf_s(version, L"%03d (library)", DIRECTX_TEX_VERSION); } if (versionOnly) { wprintf(L"texdiag version %ls\n", version); } else { wprintf(L"Microsoft (R) DirectX Texture Diagnostic Tool [DirectXTex] Version %ls\n", version); wprintf(L"Copyright (C) Microsoft Corp.\n"); #ifdef _DEBUG wprintf(L"*** Debug build ***\n"); #endif wprintf(L"\n"); } } void PrintUsage() { PrintLogo(false); static const wchar_t* const s_usage = L"Usage: texdiag [--] \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 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" -xlum expand legacy L8, L16, and A8P8 formats\n" L"\n" L" (diff only)\n" L" -f format\n" L" -o output filename\n" L" -l force output filename to lower case\n" L" -y overwrite existing output file (if any)\n" L" -c highlight difference color (defaults to off)\n" L" -t highlight threshold (defaults to 0.25)\n" L"\n" L" (dumpbc only)\n" L" -targetx dump pixels at location x (defaults to all)\n" L" -targety dump pixels at location y (defaults to all)\n" L"\n" L" (dumpdds only)\n" L" -ft output file type\n" L"\n" L" -nologo suppress copyright message\n" L" -flist 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 : "); PrintList(13, g_pFormats); wprintf(L" "); PrintList(13, g_pFormatAliases); wprintf(L"\n : "); PrintList(13, g_pFilters); wprintf(L"\n : "); PrintList(15, g_pDumpFileTypes); } const wchar_t* GetErrorDesc(HRESULT hr) { static wchar_t desc[1024] = {}; LPWSTR errorText = nullptr; const DWORD result = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER, nullptr, static_cast(hr), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast(&errorText), 0, nullptr); *desc = 0; if (result > 0 && errorText) { swprintf_s(desc, L": %ls", errorText); size_t len = wcslen(desc); if (len >= 1) { desc[len - 1] = 0; } if (errorText) LocalFree(errorText); for(wchar_t* ptr = desc; *ptr != 0; ++ptr) { if (*ptr == L'\r' || *ptr == L'\n') { *ptr = L' '; } } } return desc; } HRESULT LoadImage( const wchar_t *fileName, uint32_t dwOptions, TEX_FILTER_FLAGS dwFilter, TexMetadata& info, std::unique_ptr& 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; 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 else { // WIC shares the same filter values for mode and dither static_assert(static_cast(WIC_FLAGS_DITHER) == static_cast(TEX_FILTER_DITHER), "WIC_FLAGS_* & TEX_FILTER_* should match"); static_assert(static_cast(WIC_FLAGS_DITHER_DIFFUSION) == static_cast(TEX_FILTER_DITHER_DIFFUSION), "WIC_FLAGS_* & TEX_FILTER_* should match"); static_assert(static_cast(WIC_FLAGS_FILTER_POINT) == static_cast(TEX_FILTER_POINT), "WIC_FLAGS_* & TEX_FILTER_* should match"); static_assert(static_cast(WIC_FLAGS_FILTER_LINEAR) == static_cast(TEX_FILTER_LINEAR), "WIC_FLAGS_* & TEX_FILTER_* should match"); static_assert(static_cast(WIC_FLAGS_FILTER_CUBIC) == static_cast(TEX_FILTER_CUBIC), "WIC_FLAGS_* & TEX_FILTER_* should match"); static_assert(static_cast(WIC_FLAGS_FILTER_FANT) == static_cast(TEX_FILTER_FANT), "WIC_FLAGS_* & TEX_FILTER_* should match"); HRESULT hr = LoadFromWICFile(fileName, dwFilter | WIC_FLAGS_ALL_FRAMES, &info, *image); if (hr == static_cast(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 default: return SaveToWICFile(*image, WIC_FLAGS_NONE, GetWICCodec(static_cast(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); 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(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(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(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(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(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(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(&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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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); 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 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); 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(LookupByName(pValue, g_pFormats)); if (!diffFormat) { diffFormat = static_cast(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(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); 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); 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 image1; hr = LoadImage(pImage1->szSrc.c_str(), dwOptions, dwFilter, info1, image1); if (FAILED(hr)) { wprintf(L" FAILED (%08X%ls)\n", static_cast(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 image2; hr = LoadImage(pImage2->szSrc.c_str(), dwOptions, dwFilter, info2, image2); if (FAILED(hr)) { wprintf(L" FAILED (%08X%ls)\n", static_cast(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(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(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(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(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(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 image; hr = LoadImage(curpath.c_str(), dwOptions, dwFilter, info, image); if (FAILED(hr)) { wprintf(L" FAILED (%08X%ls)\n", static_cast(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); 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(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(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(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); 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(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(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 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(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(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(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(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(hr), GetErrorDesc(hr)); return 1; } data.Print(img->format); } wprintf(L"\n"); } } } } } break; } return 0; }