1
0
mirror of https://github.com/microsoft/DirectXTex synced 2024-11-09 22:40:06 +00:00

Portable PixMap reader/writers for texconv (#180)

This commit is contained in:
Chuck Walbourn 2020-06-04 20:07:21 -07:00 committed by GitHub
parent 09fc5fe957
commit 0539287324
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 1232 additions and 497 deletions

View File

@ -101,7 +101,10 @@ add_executable(texassemble Texassemble/texassemble.cpp)
target_link_libraries(texassemble ${PROJECT_NAME})
source_group(texassemble REGULAR_EXPRESSION Texassemble/*.*)
add_executable(texconv Texconv/texconv.cpp)
add_executable(texconv
Texconv/texconv.cpp
Texconv/ExtendedBMP.cpp
Texconv/PortablePixMap.cpp)
target_link_libraries(texconv ${PROJECT_NAME})
source_group(texconv REGULAR_EXPRESSION Texconv/*.*)

View File

@ -296,9 +296,9 @@ const SValue g_pExtFileTypes[] =
namespace
{
inline HANDLE safe_handle(HANDLE h) { return (h == INVALID_HANDLE_VALUE) ? nullptr : h; }
inline HANDLE safe_handle(HANDLE h) noexcept { return (h == INVALID_HANDLE_VALUE) ? nullptr : h; }
struct find_closer { void operator()(HANDLE h) { assert(h != INVALID_HANDLE_VALUE); if (h) FindClose(h); } };
struct find_closer { void operator()(HANDLE h) noexcept { assert(h != INVALID_HANDLE_VALUE); if (h) FindClose(h); } };
using ScopedFindHandle = std::unique_ptr<void, find_closer>;
@ -543,6 +543,7 @@ namespace
PrintList(13, g_pFilters);
}
HRESULT SaveImageFile(const Image& img, DWORD fileType, const wchar_t* szOutputFile)
{
switch (fileType)
@ -566,6 +567,7 @@ namespace
}
}
enum
{
DM_UNDEFINED = 0,
@ -574,6 +576,7 @@ namespace
DM_PREVIOUS = 3
};
void FillRectangle(const Image& img, const RECT& destRect, uint32_t color)
{
RECT clipped =
@ -598,6 +601,7 @@ namespace
}
}
void BlendRectangle(const Image& composed, const Image& raw, const RECT& destRect)
{
using namespace DirectX::PackedVector;
@ -636,6 +640,7 @@ namespace
}
}
HRESULT LoadAnimatedGif(const wchar_t* szFile, std::vector<std::unique_ptr<ScratchImage>>& loadedImages, bool usebgcolor)
{
// https://code.msdn.microsoft.com/windowsapps/Windows-Imaging-Component-65abbc6a/

193
Texconv/ExtendedBMP.cpp Normal file
View File

@ -0,0 +1,193 @@
//--------------------------------------------------------------------------------------
// File: ExtendedBMP.cpp
//
// Utilities for reading BMP files including the DXTn unofficial "FS70"
// extension created for Microsoft flight simulators.
//
// http://www.mwgfx.co.uk/programs/dxtbmp.htm
//
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//
// http://go.microsoft.com/fwlink/?LinkId=248926
//--------------------------------------------------------------------------------------
#pragma warning(push)
#pragma warning(disable : 4005)
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#define NODRAWTEXT
#define NOMCX
#define NOSERVICE
#define NOHELP
#pragma warning(pop)
#include <Windows.h>
#include <cstdint>
#include <memory>
#include "DirectXTex.h"
using namespace DirectX;
namespace
{
struct handle_closer { void operator()(HANDLE h) noexcept { if (h) CloseHandle(h); } };
using ScopedHandle = std::unique_ptr<void, handle_closer>;
inline HANDLE safe_handle(HANDLE h) noexcept { return (h == INVALID_HANDLE_VALUE) ? nullptr : h; }
HRESULT ReadData(_In_z_ const wchar_t* szFile, std::unique_ptr<uint8_t[]>& blob, size_t& blobSize)
{
blob.reset();
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
ScopedHandle hFile(safe_handle(CreateFile2(szFile, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, nullptr)));
#else
ScopedHandle hFile(safe_handle(CreateFileW(szFile, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN, nullptr)));
#endif
if (!hFile)
{
return HRESULT_FROM_WIN32(GetLastError());
}
// Get the file size
FILE_STANDARD_INFO fileInfo;
if (!GetFileInformationByHandleEx(hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo)))
{
return HRESULT_FROM_WIN32(GetLastError());
}
// File is too big for 32-bit allocation, so reject read (4 GB should be plenty large enough)
if (fileInfo.EndOfFile.HighPart > 0)
{
return HRESULT_FROM_WIN32(ERROR_FILE_TOO_LARGE);
}
// Zero-sized files assumed to be invalid
if (fileInfo.EndOfFile.LowPart < 1)
{
return E_FAIL;
}
// Read file
blob.reset(new (std::nothrow) uint8_t[fileInfo.EndOfFile.LowPart]);
if (!blob)
{
return E_OUTOFMEMORY;
}
DWORD bytesRead = 0;
if (!ReadFile(hFile.get(), blob.get(), fileInfo.EndOfFile.LowPart, &bytesRead, nullptr))
{
return HRESULT_FROM_WIN32(GetLastError());
}
if (bytesRead != fileInfo.EndOfFile.LowPart)
{
return E_FAIL;
}
blobSize = fileInfo.EndOfFile.LowPart;
return S_OK;
}
HRESULT LoadFromExtendedBMPMemory(
_In_reads_bytes_(size) const void* pSource,
_In_ size_t size,
_Out_opt_ TexMetadata* metadata,
_Out_ ScratchImage& image)
{
// This loads from non-standard BMP files that are not supported by WIC
image.Release();
if (size < (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)))
return E_FAIL;
// Valid BMP files always start with 'BM' at the top
auto filehdr = reinterpret_cast<const BITMAPFILEHEADER*>(pSource);
if (filehdr->bfType != 0x4D42)
return E_FAIL;
if (size < filehdr->bfOffBits)
return E_FAIL;
auto header = reinterpret_cast<const BITMAPINFOHEADER*>(reinterpret_cast<const uint8_t*>(pSource) + sizeof(BITMAPFILEHEADER));
if (header->biSize != sizeof(BITMAPINFOHEADER))
return E_FAIL;
if (header->biWidth < 1 || header->biHeight < 1 || header->biPlanes != 1 || header->biBitCount != 16)
{
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN;
switch (header->biCompression)
{
case 0x31545844: // FourCC "DXT1"
format = DXGI_FORMAT_BC1_UNORM;
break;
case 0x33545844: // FourCC "DXT3"
format = DXGI_FORMAT_BC2_UNORM;
break;
case 0x35545844: // FourCC "DXT5"
format = DXGI_FORMAT_BC3_UNORM;
break;
default:
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
HRESULT hr = image.Initialize2D(format, size_t(header->biWidth), size_t(header->biHeight), 1, 1);
if (FAILED(hr))
return hr;
if (header->biSizeImage != image.GetPixelsSize())
return E_UNEXPECTED;
size_t remaining = size - filehdr->bfOffBits;
if (!remaining)
return E_FAIL;
if (remaining < image.GetPixelsSize())
return E_UNEXPECTED;
auto pixels = reinterpret_cast<const uint8_t*>(pSource) + filehdr->bfOffBits;
memcpy(image.GetPixels(), pixels, image.GetPixelsSize());
if (metadata)
{
*metadata = image.GetMetadata();
}
return S_OK;
}
}
HRESULT __cdecl LoadFromBMPEx(
_In_z_ const wchar_t* szFile,
_In_ WIC_FLAGS flags,
_Out_opt_ TexMetadata* metadata,
_Out_ ScratchImage& image) noexcept
{
std::unique_ptr<uint8_t[]> bmpData;
size_t bmpSize;
HRESULT hr = ReadData(szFile, bmpData, bmpSize);
if (FAILED(hr))
return hr;
hr = LoadFromWICMemory(bmpData.get(), bmpSize, flags, metadata, image);
if (FAILED(hr))
return hr;
hr = LoadFromExtendedBMPMemory(bmpData.get(), bmpSize, metadata, image);
if (FAILED(hr))
return hr;
return S_OK;
}

594
Texconv/PortablePixMap.cpp Normal file
View File

@ -0,0 +1,594 @@
//--------------------------------------------------------------------------------------
// File: PortablePixMap.cpp
//
// Utilities for reading & writing Portable PixMap files (PPM/PFM)
//
// http://paulbourke.net/dataformats/ppm/
// http://paulbourke.net/dataformats/pbmhdr/
//
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//
// http://go.microsoft.com/fwlink/?LinkId=248926
//--------------------------------------------------------------------------------------
#pragma warning(push)
#pragma warning(disable : 4005)
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#define NODRAWTEXT
#define NOMCX
#define NOSERVICE
#define NOHELP
#pragma warning(pop)
#include <Windows.h>
#include <cstdint>
#include <memory>
#include "DirectXTex.h"
using namespace DirectX;
namespace
{
struct handle_closer { void operator()(HANDLE h) noexcept { if (h) CloseHandle(h); } };
using ScopedHandle = std::unique_ptr<void, handle_closer>;
inline HANDLE safe_handle(HANDLE h) noexcept { return (h == INVALID_HANDLE_VALUE) ? nullptr : h; }
class auto_delete_file
{
public:
auto_delete_file(HANDLE hFile) noexcept : m_handle(hFile) {}
auto_delete_file(const auto_delete_file&) = delete;
auto_delete_file& operator=(const auto_delete_file&) = delete;
~auto_delete_file()
{
if (m_handle)
{
FILE_DISPOSITION_INFO info = {};
info.DeleteFile = TRUE;
(void)SetFileInformationByHandle(m_handle, FileDispositionInfo, &info, sizeof(info));
}
}
void clear() noexcept { m_handle = nullptr; }
private:
HANDLE m_handle;
};
inline size_t FindEOL(_In_z_ const char* pString, size_t max)
{
size_t pos = 0;
//find endl
while (pos < max)
{
if (pString[pos] == '\n')
return pos;
pos++;
}
return 0;
}
HRESULT ReadData(_In_z_ const wchar_t* szFile, std::unique_ptr<uint8_t[]>& blob, size_t& blobSize)
{
blob.reset();
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
ScopedHandle hFile(safe_handle(CreateFile2(szFile, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, nullptr)));
#else
ScopedHandle hFile(safe_handle(CreateFileW(szFile, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN, nullptr)));
#endif
if (!hFile)
{
return HRESULT_FROM_WIN32(GetLastError());
}
// Get the file size
FILE_STANDARD_INFO fileInfo;
if (!GetFileInformationByHandleEx(hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo)))
{
return HRESULT_FROM_WIN32(GetLastError());
}
// File is too big for 32-bit allocation, so reject read (4 GB should be plenty large enough)
if (fileInfo.EndOfFile.HighPart > 0)
{
return HRESULT_FROM_WIN32(ERROR_FILE_TOO_LARGE);
}
// Zero-sized files assumed to be invalid
if (fileInfo.EndOfFile.LowPart < 1)
{
return E_FAIL;
}
// Read file
blob.reset(new (std::nothrow) uint8_t[fileInfo.EndOfFile.LowPart]);
if (!blob)
{
return E_OUTOFMEMORY;
}
DWORD bytesRead = 0;
if (!ReadFile(hFile.get(), blob.get(), fileInfo.EndOfFile.LowPart, &bytesRead, nullptr))
{
return HRESULT_FROM_WIN32(GetLastError());
}
if (bytesRead != fileInfo.EndOfFile.LowPart)
{
return E_FAIL;
}
blobSize = fileInfo.EndOfFile.LowPart;
return S_OK;
}
}
//============================================================================
// PPM
//============================================================================
HRESULT __cdecl LoadFromPortablePixMap(
_In_z_ const wchar_t* szFile,
_Out_opt_ TexMetadata* metadata,
_Out_ ScratchImage& image) noexcept
{
std::unique_ptr<uint8_t[]> ppmData;
size_t ppmSize;
HRESULT hr = ReadData(szFile, ppmData, ppmSize);
if (FAILED(hr))
return hr;
if (ppmSize < 3)
return E_FAIL;
if (ppmData[0] != 'P' || (ppmData[1] != '3' && ppmData[1] != '6'))
return E_FAIL;
bool ascii = ppmData[1] == '3';
enum
{
PPM_WIDTH, PPM_HEIGHT, PPM_MAX, PPM_DATA_R, PPM_DATA_G, PPM_DATA_B
};
int mode = PPM_WIDTH;
auto pData = ppmData.get() + 2;
ppmSize -= 2;
size_t width = 0;
uint32_t max = 255;
uint32_t *pixels = nullptr;
uint32_t *pixelEnd = nullptr;
while (ppmSize > 0)
{
if (!ascii && mode == PPM_DATA_R)
{
// Binary data
if (max > 255 || !pixels || !pixelEnd)
return E_UNEXPECTED;
if (ppmSize > 1 && '\r' == *pData)
{
pData++;
ppmSize--;
}
if (*pData != '\n')
return E_FAIL;
pData++;
ppmSize--;
while (ppmSize > 0 && (pixels < pixelEnd))
{
*pixels++ = (255 * pData[0] / max)
| ((255 * pData[1] / max) << 8)
| ((255 * pData[2] / max) << 16)
| 0xff000000;
pData += 3;
ppmSize -= 3;
}
return (pixels != pixelEnd) ? E_FAIL : S_OK;
}
if (isspace(*pData))
{
// Whitespace
pData++;
ppmSize--;
}
else if (*pData == '#')
{
// Comment
while (ppmSize > 0 && *pData != '\n')
{
pData++;
ppmSize--;
}
if (ppmSize > 0)
{
pData++;
ppmSize--;
}
}
else
{
// ASCII number
uint32_t u = 0;
while (ppmSize > 0 && !isspace(*pData))
{
if (!isdigit(*pData))
return E_FAIL;
u = u * 10 + (*pData - '0');
pData++;
ppmSize--;
}
switch (mode)
{
case PPM_WIDTH:
if (u == 0)
return E_FAIL;
width = u;
break;
case PPM_HEIGHT:
{
if (u == 0)
return E_FAIL;
if (metadata)
{
*metadata = {};
metadata->width = width;
metadata->height = u;
metadata->depth = metadata->arraySize = metadata->mipLevels = 1;
metadata->format = DXGI_FORMAT_R8G8B8A8_UNORM;
metadata->dimension = TEX_DIMENSION_TEXTURE2D;
}
hr = image.Initialize2D(DXGI_FORMAT_R8G8B8A8_UNORM, width, u, 1, 1);
if (FAILED(hr))
return hr;
auto img = image.GetImage(0, 0, 0);
pixels = reinterpret_cast<uint32_t*>(img->pixels);
pixelEnd = pixels + width * u;
}
break;
case PPM_MAX:
if (u == 0)
return E_FAIL;
max = u;
break;
case PPM_DATA_R:
if (pixels >= pixelEnd)
return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
*pixels = ((u * 255) / max) | 0xff000000;
break;
case PPM_DATA_G:
if (pixels >= pixelEnd)
return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
*pixels |= ((u * 255) / max) << 8;
break;
case PPM_DATA_B:
if (pixels >= pixelEnd)
return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
*pixels |= ((u * 255) / max) << 16;
if (++pixels == pixelEnd)
return S_OK;
mode = PPM_DATA_R - 1;
break;
}
mode++;
}
}
return E_FAIL;
}
HRESULT __cdecl SaveToPortablePixMap(
_In_ const Image& image,
_In_z_ const wchar_t* szFile) noexcept
{
switch (image.format)
{
case DXGI_FORMAT_R8G8B8A8_UNORM:
case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
case DXGI_FORMAT_B8G8R8A8_UNORM:
case DXGI_FORMAT_B8G8R8X8_UNORM:
case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB:
break;
default:
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
char header[256] = {};
int len = sprintf_s(header, "P6\n%zu %zu\n255\n", image.width, image.height);
if (len == -1)
return E_UNEXPECTED;
ScratchImage tmpImage;
if (image.format == DXGI_FORMAT_R8G8B8A8_UNORM || image.format == DXGI_FORMAT_R8G8B8A8_UNORM_SRGB)
{
tmpImage.InitializeFromImage(image);
}
else
{
HRESULT hr = Convert(image,
IsSRGB(image.format) ? DXGI_FORMAT_R8G8B8A8_UNORM_SRGB : DXGI_FORMAT_R8G8B8A8_UNORM,
TEX_FILTER_DEFAULT, 0.f, tmpImage);
if (FAILED(hr))
return hr;
}
ScratchImage data;
data.Initialize2D(DXGI_FORMAT_R8G8B8A8_UNORM, image.width, image.height, 1, 1, CP_FLAGS_24BPP);
const auto& img = tmpImage.GetImage(0, 0, 0);
auto dptr = data.GetPixels();
for (size_t y = 0; y < image.height; ++y)
{
auto sptr = img->pixels + y * image.rowPitch;
for (size_t x = 0; x < image.width; ++x)
{
*(dptr++) = sptr[0];
*(dptr++) = sptr[1];
*(dptr++) = sptr[2];
sptr += 4;
}
}
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
ScopedHandle hFile(safe_handle(CreateFile2(szFile, GENERIC_WRITE, 0, CREATE_ALWAYS, nullptr)));
#else
ScopedHandle hFile(safe_handle(CreateFileW(szFile, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, 0, nullptr)));
#endif
if (!hFile)
return HRESULT_FROM_WIN32(GetLastError());
auto_delete_file delonfail(hFile.get());
DWORD bytesWritten;
if (!WriteFile(hFile.get(), header, static_cast<DWORD>(len), &bytesWritten, nullptr))
return HRESULT_FROM_WIN32(GetLastError());
if (!WriteFile(hFile.get(), data.GetPixels(), static_cast<DWORD>(data.GetPixelsSize()), &bytesWritten, nullptr))
return HRESULT_FROM_WIN32(GetLastError());
delonfail.clear();
return S_OK;
}
//============================================================================
// PFM
//============================================================================
HRESULT __cdecl LoadFromPortablePixMapHDR(
_In_z_ const wchar_t* szFile,
_Out_opt_ TexMetadata* metadata,
_Out_ ScratchImage& image) noexcept
{
std::unique_ptr<uint8_t[]> pfmData;
size_t pfmSize;
HRESULT hr = ReadData(szFile, pfmData, pfmSize);
if (FAILED(hr))
return hr;
if (pfmSize < 3)
return E_FAIL;
if (pfmData[0] != 'P' || (pfmData[1] != 'f' && pfmData[1] != 'F') || pfmData[2] != '\n')
return E_FAIL;
bool monochrome = pfmData[1] == 'f';
auto pData = reinterpret_cast<const char*>(pfmData.get()) + 3;
pfmSize -= 3;
size_t len = FindEOL(pData, 256);
if (!len)
return E_FAIL;
char dataStr[256] = {};
char junkStr[256] = {};
strncpy_s(dataStr, pData, len + 1);
size_t width = 0, height = 0;
if (sscanf_s(dataStr, "%zu %zu%s", &width, &height, junkStr, 256) != 2)
return E_FAIL;
pData += len + 1;
pfmSize -= len + 1;
if (!pfmSize)
return E_FAIL;
len = FindEOL(pData, 256);
if (!len)
return E_FAIL;
strncpy_s(dataStr, pData, len + 1);
float aspectRatio = 0.f;
if (sscanf_s(dataStr, "%f%s", &aspectRatio, junkStr, 256) != 1)
return E_FAIL;
bool bigendian = (aspectRatio >= 0);
pData += len + 1;
pfmSize -= len + 1;
if (!pfmSize)
return E_FAIL;
size_t scanline = width * sizeof(float) * (monochrome ? 1 : 3);
if (pfmSize < scanline * height)
return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
if (metadata)
{
*metadata = {};
metadata->width = width;
metadata->height = height;
metadata->depth = metadata->arraySize = metadata->mipLevels = 1;
metadata->format = monochrome ? DXGI_FORMAT_R32_FLOAT : DXGI_FORMAT_R32G32B32A32_FLOAT;
metadata->dimension = TEX_DIMENSION_TEXTURE2D;
}
hr = image.Initialize2D(monochrome ? DXGI_FORMAT_R32_FLOAT : DXGI_FORMAT_R32G32B32A32_FLOAT,
width, height, 1, 1);
if (FAILED(hr))
return hr;
auto img = image.GetImage(0, 0, 0);
auto sptr = reinterpret_cast<const uint32_t*>(pData);
if (monochrome)
{
for (size_t y = 0; y < height; ++y)
{
auto dptr = reinterpret_cast<uint32_t*>(img->pixels + (height - y - 1) * img->rowPitch);
for (size_t x = 0; x < width; ++x)
{
*dptr++ = (bigendian) ? _byteswap_ulong(*sptr++) : *sptr++;
}
}
}
else
{
for (size_t y = 0; y < height; ++y)
{
auto dptr = reinterpret_cast<uint32_t*>(img->pixels + (height - y - 1) * img->rowPitch);
for (size_t x = 0; x < width; ++x)
{
if (bigendian)
{
dptr[0] = _byteswap_ulong(sptr[0]);
dptr[1] = _byteswap_ulong(sptr[1]);
dptr[2] = _byteswap_ulong(sptr[2]);
}
else
{
dptr[0] = sptr[0];
dptr[1] = sptr[1];
dptr[2] = sptr[2];
}
dptr[3] = 0x3f800000; // 1.f
sptr += 3;
dptr += 4;
}
}
}
return S_OK;
}
HRESULT __cdecl SaveToPortablePixMapHDR(
_In_ const Image& image,
_In_z_ const wchar_t* szFile) noexcept
{
switch (image.format)
{
case DXGI_FORMAT_R32G32B32A32_FLOAT:
case DXGI_FORMAT_R32G32B32_FLOAT:
case DXGI_FORMAT_R16G16B16A16_FLOAT:
case DXGI_FORMAT_R32_FLOAT:
break;
default:
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
char header[256] = {};
int len = sprintf_s(header, "P%c\n%zu %zu\n-1.000000\n",
(image.format == DXGI_FORMAT_R32_FLOAT) ? 'f' : 'F',
image.width, image.height);
if (len == -1)
return E_UNEXPECTED;
ScratchImage tmpImage;
if (image.format == DXGI_FORMAT_R32_FLOAT || image.format == DXGI_FORMAT_R32G32B32_FLOAT)
{
tmpImage.InitializeFromImage(image);
}
else
{
HRESULT hr = Convert(image, DXGI_FORMAT_R32G32B32_FLOAT, TEX_FILTER_DEFAULT, 0.f, tmpImage);
if (FAILED(hr))
return hr;
}
ScratchImage flipImage;
HRESULT hr = FlipRotate(*tmpImage.GetImage(0, 0, 0), TEX_FR_FLIP_VERTICAL, flipImage);
if (FAILED(hr))
return hr;
tmpImage.Release();
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
ScopedHandle hFile(safe_handle(CreateFile2(szFile, GENERIC_WRITE, 0, CREATE_ALWAYS, nullptr)));
#else
ScopedHandle hFile(safe_handle(CreateFileW(szFile, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, 0, nullptr)));
#endif
if (!hFile)
return HRESULT_FROM_WIN32(GetLastError());
auto_delete_file delonfail(hFile.get());
DWORD bytesWritten;
if (!WriteFile(hFile.get(), header, static_cast<DWORD>(len), &bytesWritten, nullptr))
return HRESULT_FROM_WIN32(GetLastError());
if (!WriteFile(hFile.get(), flipImage.GetPixels(), static_cast<DWORD>(flipImage.GetPixelsSize()), &bytesWritten, nullptr))
return HRESULT_FROM_WIN32(GetLastError());
delonfail.clear();
return S_OK;
}

View File

@ -322,6 +322,8 @@
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="ExtendedBMP.cpp" />
<ClCompile Include="PortablePixMap.cpp" />
<ClCompile Include="Texconv.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -8,6 +8,8 @@
</ItemGroup>
<ItemGroup>
<ClCompile Include="Texconv.cpp" />
<ClCompile Include="PortablePixMap.cpp" />
<ClCompile Include="ExtendedBMP.cpp" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Texconv.rc">

View File

@ -328,6 +328,8 @@
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="ExtendedBMP.cpp" />
<ClCompile Include="PortablePixMap.cpp" />
<ClCompile Include="Texconv.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -8,6 +8,8 @@
</ItemGroup>
<ItemGroup>
<ClCompile Include="Texconv.cpp" />
<ClCompile Include="PortablePixMap.cpp" />
<ClCompile Include="ExtendedBMP.cpp" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Texconv.rc">

View File

@ -53,8 +53,10 @@ using namespace DirectX;
using namespace DirectX::PackedVector;
using Microsoft::WRL::ComPtr;
enum OPTIONS
namespace
{
enum OPTIONS
{
OPT_RECURSIVE = 1,
OPT_FILELIST,
OPT_WIDTH,
@ -111,38 +113,34 @@ enum OPTIONS
OPT_PAPER_WHITE_NITS,
OPT_BCNONMULT4FIX,
OPT_MAX
};
};
enum
{
enum
{
ROTATE_709_TO_HDR10 = 1,
ROTATE_HDR10_TO_709,
ROTATE_709_TO_2020,
ROTATE_2020_TO_709,
ROTATE_P3_TO_HDR10,
ROTATE_P3_TO_2020,
};
};
static_assert(OPT_MAX <= 64, "dwOptions is a DWORD64 bitfield");
static_assert(OPT_MAX <= 64, "dwOptions is a DWORD64 bitfield");
struct SConversion
{
struct SConversion
{
wchar_t szSrc[MAX_PATH];
wchar_t szDest[MAX_PATH];
};
};
struct SValue
{
struct SValue
{
LPCWSTR pName;
DWORD dwValue;
};
};
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
const SValue g_pOptions[] =
{
const SValue g_pOptions[] =
{
{ L"r", OPT_RECURSIVE },
{ L"flist", OPT_FILELIST },
{ L"w", OPT_WIDTH },
@ -199,12 +197,12 @@ const SValue g_pOptions[] =
{ L"nits", OPT_PAPER_WHITE_NITS },
{ L"fixbc4x4", OPT_BCNONMULT4FIX},
{ nullptr, 0 }
};
};
#define DEFFMT(fmt) { L## #fmt, DXGI_FORMAT_ ## fmt }
const SValue g_pFormats[] =
{
const SValue g_pFormats[] =
{
// List does not include _TYPELESS or depth/stencil formats
DEFFMT(R32G32B32A32_FLOAT),
DEFFMT(R32G32B32A32_UINT),
@ -288,10 +286,10 @@ const SValue g_pFormats[] =
DEFFMT(B4G4R4A4_UNORM),
{ nullptr, DXGI_FORMAT_UNKNOWN }
};
};
const SValue g_pFormatAliases[] =
{
const SValue g_pFormatAliases[] =
{
{ L"DXT1", DXGI_FORMAT_BC1_UNORM },
{ L"DXT2", DXGI_FORMAT_BC2_UNORM },
{ L"DXT3", DXGI_FORMAT_BC2_UNORM },
@ -308,10 +306,10 @@ const SValue g_pFormatAliases[] =
{ L"BPTC_FLOAT", DXGI_FORMAT_BC6H_UF16 },
{ nullptr, DXGI_FORMAT_UNKNOWN }
};
};
const SValue g_pReadOnlyFormats[] =
{
const SValue g_pReadOnlyFormats[] =
{
DEFFMT(R32G32B32A32_TYPELESS),
DEFFMT(R32G32B32_TYPELESS),
DEFFMT(R16G16B16A16_TYPELESS),
@ -357,10 +355,10 @@ const SValue g_pReadOnlyFormats[] =
{ L"V408", DXGI_FORMAT(132) },
{ nullptr, DXGI_FORMAT_UNKNOWN }
};
};
const SValue g_pFilters[] =
{
const SValue g_pFilters[] =
{
{ L"POINT", TEX_FILTER_POINT },
{ L"LINEAR", TEX_FILTER_LINEAR },
{ L"CUBIC", TEX_FILTER_CUBIC },
@ -380,10 +378,10 @@ const SValue g_pFilters[] =
{ 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 }
};
};
const SValue g_pRotateColor[] =
{
const SValue g_pRotateColor[] =
{
{ L"709to2020", ROTATE_709_TO_2020 },
{ L"2020to709", ROTATE_2020_TO_709 },
{ L"709toHDR10", ROTATE_709_TO_HDR10 },
@ -391,20 +389,22 @@ const SValue g_pRotateColor[] =
{ L"P3to2020", ROTATE_P3_TO_2020 },
{ L"P3toHDR10", ROTATE_P3_TO_HDR10 },
{ nullptr, 0 },
};
};
#define CODEC_DDS 0xFFFF0001
#define CODEC_TGA 0xFFFF0002
#define CODEC_HDP 0xFFFF0003
#define CODEC_JXR 0xFFFF0004
#define CODEC_HDR 0xFFFF0005
#define CODEC_PPM 0xFFFF0006
#define CODEC_PFM 0xFFFF0007
#ifdef USE_OPENEXR
#define CODEC_EXR 0xFFFF0006
#define CODEC_EXR 0xFFFF0008
#endif
const SValue g_pSaveFileTypes[] = // valid formats to write to
{
const SValue g_pSaveFileTypes[] = // valid formats to write to
{
{ L"BMP", WIC_CODEC_BMP },
{ L"JPG", WIC_CODEC_JPEG },
{ L"JPEG", WIC_CODEC_JPEG },
@ -417,14 +417,16 @@ const SValue g_pSaveFileTypes[] = // valid formats to write to
{ L"WDP", WIC_CODEC_WMP },
{ L"HDP", CODEC_HDP },
{ L"JXR", CODEC_JXR },
#ifdef USE_OPENEXR
{ L"PPM", CODEC_PPM },
{ L"PFM", CODEC_PFM },
#ifdef USE_OPENEXR
{ L"EXR", CODEC_EXR },
#endif
#endif
{ nullptr, CODEC_DDS }
};
};
const SValue g_pFeatureLevels[] = // valid feature levels for -fl for maximimum size
{
const SValue g_pFeatureLevels[] = // valid feature levels for -fl for maximimum size
{
{ L"9.1", 2048 },
{ L"9.2", 2048 },
{ L"9.3", 4096 },
@ -435,7 +437,36 @@ const SValue g_pFeatureLevels[] = // valid feature levels for -fl for maximimu
{ L"12.0", 16384 },
{ L"12.1", 16384 },
{ nullptr, 0 },
};
};
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
HRESULT __cdecl LoadFromBMPEx(
_In_z_ const wchar_t* szFile,
_In_ WIC_FLAGS flags,
_Out_opt_ TexMetadata* metadata,
_Out_ ScratchImage& image) noexcept;
HRESULT __cdecl LoadFromPortablePixMap(
_In_z_ const wchar_t* szFile,
_Out_opt_ TexMetadata* metadata,
_Out_ ScratchImage& image) noexcept;
HRESULT __cdecl SaveToPortablePixMap(
_In_ const Image& image,
_In_z_ const wchar_t* szFile) noexcept;
HRESULT __cdecl LoadFromPortablePixMapHDR(
_In_z_ const wchar_t* szFile,
_Out_opt_ TexMetadata* metadata,
_Out_ ScratchImage& image) noexcept;
HRESULT __cdecl SaveToPortablePixMapHDR(
_In_ const Image& image,
_In_z_ const wchar_t* szFile) noexcept;
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
@ -445,13 +476,9 @@ const SValue g_pFeatureLevels[] = // valid feature levels for -fl for maximimu
namespace
{
inline HANDLE safe_handle(HANDLE h) { return (h == INVALID_HANDLE_VALUE) ? nullptr : h; }
inline HANDLE safe_handle(HANDLE h) noexcept { return (h == INVALID_HANDLE_VALUE) ? nullptr : h; }
struct handle_closer { void operator()(HANDLE h) { assert(h != INVALID_HANDLE_VALUE); if (h) CloseHandle(h); } };
using ScopedHandle = std::unique_ptr<void, handle_closer>;
struct find_closer { void operator()(HANDLE h) { assert(h != INVALID_HANDLE_VALUE); if (h) FindClose(h); } };
struct find_closer { void operator()(HANDLE h) noexcept { assert(h != INVALID_HANDLE_VALUE); if (h) FindClose(h); } };
using ScopedFindHandle = std::unique_ptr<void, find_closer>;
@ -984,127 +1011,6 @@ namespace
float normalizedLinear = pow(std::max(pow(abs(ST2084), 1.0f / 78.84375f) - 0.8359375f, 0.0f) / (18.8515625f - 18.6875f * pow(abs(ST2084), 1.0f / 78.84375f)), 1.0f / 0.1593017578f);
return normalizedLinear;
}
HRESULT ReadData(_In_z_ const wchar_t* szFile, std::unique_ptr<uint8_t[]>& blob, size_t& bmpSize)
{
blob.reset();
ScopedHandle hFile(safe_handle(CreateFileW(szFile, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN, nullptr)));
if (!hFile)
{
return HRESULT_FROM_WIN32(GetLastError());
}
// Get the file size
FILE_STANDARD_INFO fileInfo;
if (!GetFileInformationByHandleEx(hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo)))
{
return HRESULT_FROM_WIN32(GetLastError());
}
// File is too big for 32-bit allocation, so reject read (4 GB should be plenty large enough)
if (fileInfo.EndOfFile.HighPart > 0)
{
return HRESULT_FROM_WIN32(ERROR_FILE_TOO_LARGE);
}
// Zero-sized files assumed to be invalid
if (fileInfo.EndOfFile.LowPart < 1)
{
return E_FAIL;
}
// Read file
blob.reset(new (std::nothrow) uint8_t[fileInfo.EndOfFile.LowPart]);
if (!blob)
{
return E_OUTOFMEMORY;
}
DWORD bytesRead = 0;
if (!ReadFile(hFile.get(), blob.get(), fileInfo.EndOfFile.LowPart, &bytesRead, nullptr))
{
return HRESULT_FROM_WIN32(GetLastError());
}
if (bytesRead != fileInfo.EndOfFile.LowPart)
{
return E_FAIL;
}
bmpSize = fileInfo.EndOfFile.LowPart;
return S_OK;
}
HRESULT LoadFromExtendedBMPMemory(_In_reads_bytes_(size) const void* pSource, _In_ size_t size, _Out_opt_ TexMetadata* metadata, _Out_ ScratchImage& image)
{
// This loads from non-standard BMP files that are not supported by WIC
image.Release();
if (size < (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)))
return E_FAIL;
// Valid BMP files always start with 'BM' at the top
auto filehdr = reinterpret_cast<const BITMAPFILEHEADER*>(pSource);
if (filehdr->bfType != 0x4D42)
return E_FAIL;
if (size < filehdr->bfOffBits)
return E_FAIL;
auto header = reinterpret_cast<const BITMAPINFOHEADER*>(reinterpret_cast<const uint8_t*>(pSource) + sizeof(BITMAPFILEHEADER));
if (header->biSize != sizeof(BITMAPINFOHEADER))
return E_FAIL;
if (header->biWidth < 1 || header->biHeight < 1 || header->biPlanes != 1 || header->biBitCount != 16)
{
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN;
switch (header->biCompression)
{
case 0x31545844: // FourCC "DXT1"
format = DXGI_FORMAT_BC1_UNORM;
break;
case 0x33545844: // FourCC "DXT3"
format = DXGI_FORMAT_BC2_UNORM;
break;
case 0x35545844: // FourCC "DXT5"
format = DXGI_FORMAT_BC3_UNORM;
break;
default:
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
HRESULT hr = image.Initialize2D(format, size_t(header->biWidth), size_t(header->biHeight), 1, 1);
if (FAILED(hr))
return hr;
if (header->biSizeImage != image.GetPixelsSize())
return E_UNEXPECTED;
size_t remaining = size - filehdr->bfOffBits;
if (!remaining)
return E_FAIL;
if (remaining < image.GetPixelsSize())
return E_UNEXPECTED;
auto pixels = reinterpret_cast<const uint8_t*>(pSource) + filehdr->bfOffBits;
memcpy(image.GetPixels(), pixels, image.GetPixelsSize());
if (metadata)
{
*metadata = image.GetMetadata();
}
return S_OK;
}
}
//--------------------------------------------------------------------------------------
@ -1803,20 +1709,7 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
}
else if (_wcsicmp(ext, L".bmp") == 0)
{
std::unique_ptr<uint8_t[]> bmpData;
size_t bmpSize;
hr = ReadData(pConv->szSrc, bmpData, bmpSize);
if (SUCCEEDED(hr))
{
hr = LoadFromWICMemory(bmpData.get(), bmpSize, WIC_FLAGS_NONE | dwFilter, &info, *image);
if (FAILED(hr))
{
if (SUCCEEDED(LoadFromExtendedBMPMemory(bmpData.get(), bmpSize, &info, *image)))
{
hr = S_OK;
}
}
}
hr = LoadFromBMPEx(pConv->szSrc, WIC_FLAGS_NONE | dwFilter, &info, *image);
if (FAILED(hr))
{
wprintf(L" FAILED (%x)\n", static_cast<unsigned int>(hr));
@ -1841,6 +1734,24 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
continue;
}
}
else if (_wcsicmp(ext, L".ppm") == 0)
{
hr = LoadFromPortablePixMap(pConv->szSrc, &info, *image);
if (FAILED(hr))
{
wprintf(L" FAILED (%x)\n", static_cast<unsigned int>(hr));
continue;
}
}
else if (_wcsicmp(ext, L".pfm") == 0)
{
hr = LoadFromPortablePixMapHDR(pConv->szSrc, &info, *image);
if (FAILED(hr))
{
wprintf(L" FAILED (%x)\n", static_cast<unsigned int>(hr));
continue;
}
}
#ifdef USE_OPENEXR
else if (_wcsicmp(ext, L".exr") == 0)
{
@ -3146,6 +3057,14 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
hr = SaveToHDRFile(img[0], pConv->szDest);
break;
case CODEC_PPM:
hr = SaveToPortablePixMap(img[0], pConv->szDest);
break;
case CODEC_PFM:
hr = SaveToPortablePixMapHDR(img[0], pConv->szDest);
break;
#ifdef USE_OPENEXR
case CODEC_EXR:
hr = SaveToEXRFile(img[0], pConv->szDest);

View File

@ -361,9 +361,9 @@ const SValue g_pExtFileTypes[] =
namespace
{
inline HANDLE safe_handle(HANDLE h) { return (h == INVALID_HANDLE_VALUE) ? nullptr : h; }
inline HANDLE safe_handle(HANDLE h) noexcept { return (h == INVALID_HANDLE_VALUE) ? nullptr : h; }
struct find_closer { void operator()(HANDLE h) { assert(h != INVALID_HANDLE_VALUE); if (h) FindClose(h); } };
struct find_closer { void operator()(HANDLE h) noexcept { assert(h != INVALID_HANDLE_VALUE); if (h) FindClose(h); } };
using ScopedFindHandle = std::unique_ptr<void, find_closer>;
@ -573,7 +573,12 @@ namespace
PrintList(15, g_pDumpFileTypes);
}
HRESULT LoadImage(const wchar_t *fileName, DWORD dwOptions, TEX_FILTER_FLAGS dwFilter, TexMetadata& info, std::unique_ptr<ScratchImage>& image)
HRESULT LoadImage(
const wchar_t *fileName,
DWORD dwOptions,
TEX_FILTER_FLAGS dwFilter,
TexMetadata& info,
std::unique_ptr<ScratchImage>& image)
{
if (!fileName)
return E_INVALIDARG;
@ -1233,7 +1238,12 @@ namespace
//--------------------------------------------------------------------------------------
HRESULT Difference(const Image& image1, const Image& image2, TEX_FILTER_FLAGS dwFilter, DXGI_FORMAT format, ScratchImage& result)
HRESULT Difference(
const Image& image1,
const Image& image2,
TEX_FILTER_FLAGS dwFilter,
DXGI_FORMAT format,
ScratchImage& result)
{
if (!image1.pixels || !image2.pixels)
return E_POINTER;
@ -1370,7 +1380,10 @@ namespace
}
};
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)
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++)
{