Added HDR readers and writes and Evaluate function

This commit is contained in:
Chuck Walbourn 2016-09-10 20:43:33 -07:00
parent b0d443f526
commit 04143d621c
20 changed files with 1162 additions and 8 deletions

View File

@ -31,6 +31,8 @@
#include <d3d11_1.h>
#endif
#include <directxmath.h>
#include <ocidl.h>
#define DIRECTX_TEX_VERSION 140
@ -209,6 +211,11 @@ namespace DirectX
HRESULT __cdecl GetMetadataFromDDSFile( _In_z_ const wchar_t* szFile, _In_ DWORD flags,
_Out_ TexMetadata& metadata );
HRESULT __cdecl GetMetadataFromHDRMemory( _In_reads_bytes_(size) const void* pSource, _In_ size_t size,
_Out_ TexMetadata& metadata );
HRESULT __cdecl GetMetadataFromHDRFile( _In_z_ const wchar_t* szFile,
_Out_ TexMetadata& metadata );
HRESULT __cdecl GetMetadataFromTGAMemory( _In_reads_bytes_(size) const void* pSource, _In_ size_t size,
_Out_ TexMetadata& metadata );
HRESULT __cdecl GetMetadataFromTGAFile( _In_z_ const wchar_t* szFile,
@ -304,6 +311,8 @@ namespace DirectX
void *__cdecl GetBufferPointer() const { return m_buffer; }
size_t __cdecl GetBufferSize() const { return m_size; }
HRESULT __cdecl Trim(size_t size);
private:
void* m_buffer;
size_t m_size;
@ -326,6 +335,15 @@ namespace DirectX
HRESULT __cdecl SaveToDDSFile( _In_ const Image& image, _In_ DWORD flags, _In_z_ const wchar_t* szFile );
HRESULT __cdecl SaveToDDSFile( _In_reads_(nimages) const Image* images, _In_ size_t nimages, _In_ const TexMetadata& metadata, _In_ DWORD flags, _In_z_ const wchar_t* szFile );
// HDR operations
HRESULT __cdecl LoadFromHDRMemory( _In_reads_bytes_(size) const void* pSource, _In_ size_t size,
_Out_opt_ TexMetadata* metadata, _Out_ ScratchImage& image );
HRESULT __cdecl LoadFromHDRFile( _In_z_ const wchar_t* szFile,
_Out_opt_ TexMetadata* metadata, _Out_ ScratchImage& image );
HRESULT __cdecl SaveToHDRMemory( _In_ const Image& image, _Out_ Blob& blob );
HRESULT __cdecl SaveToHDRFile( _In_ const Image& image, _In_z_ const wchar_t* szFile );
// TGA operations
HRESULT __cdecl LoadFromTGAMemory( _In_reads_bytes_(size) const void* pSource, _In_ size_t size,
_Out_opt_ TexMetadata* metadata, _Out_ ScratchImage& image );
@ -590,6 +608,9 @@ namespace DirectX
HRESULT __cdecl ComputeMSE( _In_ const Image& image1, _In_ const Image& image2, _Out_ float& mse, _Out_writes_opt_(4) float* mseV, _In_ DWORD flags = 0 );
HRESULT __cdecl Evaluate( _In_ const Image& image,
_In_ std::function<void __cdecl(_In_reads_(width) const XMVECTOR* pixels, size_t width, size_t y)> pixelFunc );
//---------------------------------------------------------------------------------
// WIC utility code

View File

@ -0,0 +1,979 @@
//-------------------------------------------------------------------------------------
// DirectXTexHDR.cpp
//
// DirectX Texture Library - Radiance HDR (RGBE) file format reader/writer
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// http://go.microsoft.com/fwlink/?LinkId=248926
//-------------------------------------------------------------------------------------
#include "directxtexp.h"
//
// In theory HDR (RGBE) Radiance files can have any of the following data orientations
//
// +X width +Y height
// +X width -Y height
// -X width +Y height
// -X width -Y height
// +Y height +X width
// -Y height +X width
// +Y height -X width
// -Y height -X width
//
// All HDR files we've encountered are always written as "-Y height +X width", so
// we support only that one as that's what other Radiance parsing code does as well.
//
//#define DISABLE_COMPRESS
using namespace DirectX;
namespace
{
const char g_Signature[] = "#?RADIANCE";
const char g_Format[] = "FORMAT=";
const char g_Exposure[] = "EXPOSURE=";
const char g_sRGBE[] = "32-bit_rle_rgbe";
const char g_sXYZE[] = "32-bit_rle_xyze";
const char g_Header[] =
"#?RADIANCE\n"\
"FORMAT=32-bit_rle_rgbe\n"\
"\n"\
"-Y %u +X %u\n";
inline size_t FindEOL(const char* str, size_t maxlen)
{
size_t pos = 0;
while (pos < maxlen)
{
if (str[pos] == '\n')
return pos;
else if (str[pos] == '\0')
return size_t(-1);
++pos;
}
return 0;
}
//-------------------------------------------------------------------------------------
// Decodes HDR header
//-------------------------------------------------------------------------------------
HRESULT DecodeHDRHeader(
_In_reads_bytes_(size) const void* pSource,
size_t size,
_Out_ TexMetadata& metadata,
size_t& offset,
float& exposure)
{
if (!pSource)
return E_INVALIDARG;
memset(&metadata, 0, sizeof(TexMetadata));
exposure = 1.f;
if (size < sizeof(g_Signature))
{
return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
}
// Verify magic signature
if (memcmp(pSource, g_Signature, sizeof(g_Signature) - 1) != 0)
{
return E_FAIL;
}
// Process first part of header
bool formatFound = false;
const char* info = reinterpret_cast<const char*>(pSource);
while (size > 0)
{
if (*info == '\n')
{
++info;
--size;
break;
}
const size_t formatLen = sizeof(g_Format) - 1;
const size_t exposureLen = sizeof(g_Exposure) - 1;
if ((size > formatLen) && memcmp(info, g_Format, formatLen) == 0)
{
info += formatLen;
size -= formatLen;
// Trim whitespace
while (*info == ' ' || *info == '\t')
{
if (--size == 0)
return E_FAIL;
++info;
}
static_assert(sizeof(g_sRGBE) == sizeof(g_sXYZE), "Format strings length mismatch");
const size_t encodingLen = sizeof(g_sRGBE) - 1;
if (size < encodingLen)
{
return E_FAIL;
}
if (memcmp(info, g_sRGBE, encodingLen) != 0 && memcmp(info, g_sXYZE, encodingLen) != 0)
{
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
formatFound = true;
size_t len = FindEOL(info, size);
if (len == size_t(-1))
{
return E_FAIL;
}
info += len + 1;
size -= len + 1;
}
else if ((size > exposureLen) && memcmp(info, g_Exposure, exposureLen) == 0)
{
info += exposureLen;
size -= exposureLen;
// Trim whitespace
while (*info == ' ' || *info == '\t')
{
if (--size == 0)
return E_FAIL;
++info;
}
size_t len = FindEOL(info, size);
if (len == size_t(-1)
|| len < 1)
{
return E_FAIL;
}
char buff[32] = {};
strncpy_s(buff, info, std::min<size_t>(31, len));
float newExposure = static_cast<float>(atof(buff));
if ((newExposure >= 1e-12) && (newExposure <= 1e12))
{
// Note that we ignore strange exposure values (like EXPOSURE=0)
exposure *= newExposure;
}
info += len + 1;
size -= len + 1;
}
else
{
size_t len = FindEOL(info, size);
if (len == size_t(-1))
{
return E_FAIL;
}
info += len + 1;
size -= len + 1;
}
}
if (!formatFound)
{
return E_FAIL;
}
// Get orientation
char orient[256] = {};
size_t len = FindEOL(info, std::min<size_t>(sizeof(orient), size - 1));
if (len == size_t(-1)
|| len <= 2)
{
return E_FAIL;
}
strncpy_s(orient, info, len);
if (orient[0] != '-' && orient[1] != 'Y')
{
// We only support the -Y +X orientation (see top of file)
return HRESULT_FROM_WIN32(
((orient[0] == '+' || orient[0] == '-') && (orient[1] == 'X' || orient[1] == 'Y'))
? ERROR_NOT_SUPPORTED : ERROR_INVALID_DATA
);
}
uint32_t height = 0;
if (sscanf_s(orient + 2, "%u", &height) != 1)
{
return E_FAIL;
}
const char* ptr = orient + 2;
while (*ptr != 0 && *ptr != '-' && *ptr != '+')
++ptr;
if (*ptr == 0)
{
return E_FAIL;
}
else if (*ptr != '+')
{
// We only support the -Y +X orientation (see top of file)
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
++ptr;
if (*ptr == 0 || (*ptr != 'X' && *ptr != 'Y'))
{
return E_FAIL;
}
else if (*ptr != 'X')
{
// We only support the -Y +X orientation (see top of file)
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
++ptr;
uint32_t width;
if (sscanf_s(ptr, "%u", &width) != 1)
{
return E_FAIL;
}
info += len + 1;
size -= len + 1;
if (!width || !height)
{
return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
}
if (size == 0)
{
return E_FAIL;
}
offset = info - reinterpret_cast<const char*>(pSource);
metadata.width = width;
metadata.height = height;
metadata.depth = metadata.arraySize = metadata.mipLevels = 1;
metadata.format = DXGI_FORMAT_R32G32B32A32_FLOAT;
metadata.dimension = TEX_DIMENSION_TEXTURE2D;
return S_OK;
}
//-------------------------------------------------------------------------------------
// FloatToRGBE
//-------------------------------------------------------------------------------------
inline void FloatToRGBE(_Out_writes_(width*4) uint8_t* pDestination, _In_reads_(width*bpp) const float* pSource, size_t width, int bpp)
{
for (size_t j = 0; j < width; ++j)
{
float r = pSource[0] >= 0.f ? pSource[0] : 0.f;
float g = pSource[1] >= 0.f ? pSource[1] : 0.f;
float b = pSource[2] >= 0.f ? pSource[2] : 0.f;
pSource += bpp;
const float max_xy = (r > g) ? r : g;
float max_xyz = (max_xy > b) ? max_xy : b;
if (max_xyz > 1e-32)
{
int e;
max_xyz = frexpf(max_xyz, &e) * 256.f / max_xyz;
e += 128;
uint8_t red = uint8_t(r * max_xyz);
uint8_t green = uint8_t(g * max_xyz);
uint8_t blue = uint8_t(b * max_xyz);
pDestination[0] = red;
pDestination[1] = green;
pDestination[2] = blue;
pDestination[3] = (red || green || blue) ? uint8_t(e & 0xff) : 0;
}
else
{
pDestination[0] = pDestination[1] = pDestination[2] = pDestination[3] = 0;
}
pDestination += 4;
}
}
//-------------------------------------------------------------------------------------
// Encode using Adapative RLE
//-------------------------------------------------------------------------------------
_Success_(return > 0)
size_t EncodeRLE(_Out_writes_(width*4) uint8_t* enc, _In_reads_(width*4) uint8_t* rgbe, size_t rowPitch, size_t width)
{
if (width < 8 || width > 32767)
{
// Don't try to compress too narrow or too wide scan-lines
return 0;
}
enc[0] = 2;
enc[1] = 2;
enc[2] = uint8_t(width >> 8);
enc[3] = uint8_t(width & 0xff);
enc += 4;
size_t encSize = 4;
uint8_t scan[128] = {};
for (int channel = 0; channel < 4; ++channel)
{
uint8_t* spanPtr = rgbe + channel;
for (size_t pixelCount = 0; pixelCount < width;)
{
uint8_t spanLen = 1;
while (pixelCount + spanLen < width && spanLen < 127)
{
if (spanPtr[spanLen * 4] == *spanPtr)
{
++spanLen;
}
else
break;
}
if (spanLen > 1)
{
if (encSize + 2 > rowPitch)
return 0;
enc[0] = 128 + spanLen;
enc[1] = *spanPtr;
enc += 2;
encSize += 2;
spanPtr += spanLen * 4;
pixelCount += spanLen;
}
else
{
uint8_t runLen = 1;
scan[0] = *spanPtr;
while (pixelCount + runLen < width && runLen < 127)
{
if (spanPtr[(runLen - 1) * 4] != spanPtr[runLen * 4])
{
scan[runLen++] = spanPtr[runLen * 4];
}
else
break;
}
if (encSize + runLen + 1 > rowPitch)
return 0;
*enc++ = runLen;
memcpy(enc, scan, runLen);
enc += runLen;
encSize += runLen + 1;
spanPtr += runLen * 4;
pixelCount += runLen;
}
}
}
return encSize;
}
}
//=====================================================================================
// Entry-points
//=====================================================================================
//-------------------------------------------------------------------------------------
// Obtain metadata from HDR file in memory/on disk
//-------------------------------------------------------------------------------------
_Use_decl_annotations_
HRESULT DirectX::GetMetadataFromHDRMemory(const void* pSource, size_t size, TexMetadata& metadata)
{
if (!pSource || size == 0)
return E_INVALIDARG;
size_t offset;
float exposure;
return DecodeHDRHeader(pSource, size, metadata, offset, exposure);
}
_Use_decl_annotations_
HRESULT DirectX::GetMetadataFromHDRFile(const wchar_t* szFile, TexMetadata& metadata)
{
if (!szFile)
return E_INVALIDARG;
#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 for a valid HDR file)
if (fileInfo.EndOfFile.HighPart > 0)
{
return HRESULT_FROM_WIN32(ERROR_FILE_TOO_LARGE);
}
// Need at least enough data to fill the standard header to be a valid HDR
if (fileInfo.EndOfFile.LowPart < sizeof(g_Signature))
{
return E_FAIL;
}
// Read the first part of the file to find the header
uint8_t header[8192];
DWORD bytesRead = 0;
if (!ReadFile(hFile.get(), header, std::min<DWORD>(sizeof(header), fileInfo.EndOfFile.LowPart), &bytesRead, nullptr))
{
return HRESULT_FROM_WIN32(GetLastError());
}
size_t offset;
float exposure;
return DecodeHDRHeader(header, bytesRead, metadata, offset, exposure);
}
//-------------------------------------------------------------------------------------
// Load a HDR file in memory
//-------------------------------------------------------------------------------------
_Use_decl_annotations_
HRESULT DirectX::LoadFromHDRMemory(const void* pSource, size_t size, TexMetadata* metadata, ScratchImage& image)
{
if (!pSource || size == 0)
return E_INVALIDARG;
image.Release();
size_t offset;
float exposure;
TexMetadata mdata;
HRESULT hr = DecodeHDRHeader(pSource, size, mdata, offset, exposure);
if (FAILED(hr))
return hr;
if (offset > size)
return E_FAIL;
size_t remaining = size - offset;
if (remaining == 0)
return E_FAIL;
hr = image.Initialize2D(mdata.format, mdata.width, mdata.height, 1, 1);
if (FAILED(hr))
return hr;
// Copy pixels
auto sourcePtr = reinterpret_cast<const uint8_t*>(pSource) + offset;
size_t pixelLen = remaining;
const Image* img = image.GetImage(0, 0, 0);
auto destPtr = img->pixels;
#ifdef _DEBUG
memset(img->pixels, 0xFF, img->rowPitch * img->height);
#endif
for (size_t scan = 0; scan < mdata.height; ++scan)
{
if (pixelLen < 4)
{
image.Release();
return E_FAIL;
}
uint8_t inColor[4];
memcpy(inColor, sourcePtr, 4);
sourcePtr += 4;
pixelLen -= 4;
auto scanLine = reinterpret_cast<float*>(destPtr);
if (inColor[0] == 2 && inColor[1] == 2 && inColor[2] < 128)
{
// Adaptive Run Length Encoding (RLE)
if (size_t((inColor[2] << 8) + inColor[3]) != mdata.width)
{
return E_FAIL;
}
for (int channel = 0; channel < 4; ++channel)
{
auto pixelLoc = scanLine + channel;
for(size_t pixelCount = 0; pixelCount < mdata.width;)
{
if (pixelLen < 2)
{
return E_FAIL;
}
assert(sourcePtr < (reinterpret_cast<const uint8_t*>(pSource) + size));
uint8_t runLen = *sourcePtr;
if (runLen > 128)
{
runLen &= 127;
if (pixelCount + runLen > mdata.width)
{
return E_FAIL;
}
float val = static_cast<float>(sourcePtr[1]);
for (uint8_t j = 0; j < runLen; ++j)
{
*pixelLoc = val;
pixelLoc += 4;
}
pixelCount += runLen;
sourcePtr += 2;
pixelLen -= 2;
}
else if ((size < size_t(runLen + 1)) || ((pixelCount + runLen) > mdata.width))
{
return E_FAIL;
}
else
{
++sourcePtr;
for (uint8_t j = 0; j < runLen; ++j)
{
float val = static_cast<float>(*sourcePtr++);
*pixelLoc = val;
pixelLoc += 4;
}
pixelCount += runLen;
pixelLen -= runLen + 1;
}
}
}
}
else
{
auto pixelLoc = scanLine;
float prevColor[4];
prevColor[0] = inColor[0];
prevColor[1] = inColor[1];
prevColor[2] = inColor[2];
prevColor[3] = inColor[3];
int bitShift = 0;
for (size_t pixelCount = 0; pixelCount < mdata.width;)
{
if (inColor[0] == 1 && inColor[1] == 1 && inColor[2] == 1)
{
// "Standard" Run Length Encoding
size_t spanLen = inColor[3] << bitShift;
if (spanLen + pixelCount > mdata.width)
{
return E_FAIL;
}
for (size_t j = 0; j < spanLen; ++j)
{
pixelLoc[0] = prevColor[0];
pixelLoc[1] = prevColor[1];
pixelLoc[2] = prevColor[2];
pixelLoc[3] = prevColor[3];
pixelLoc += 4;
}
pixelCount += spanLen;
bitShift += 8;
}
else
{
// Uncompressed
pixelLoc[0] = prevColor[0] = inColor[0];
pixelLoc[1] = prevColor[1] = inColor[1];
pixelLoc[2] = prevColor[2] = inColor[2];
pixelLoc[3] = prevColor[3] = inColor[3];
bitShift = 0;
++pixelCount;
pixelLoc += 4;
}
if (pixelCount >= mdata.width)
break;
if (pixelLen < 4)
{
return E_FAIL;
}
memcpy(inColor, sourcePtr, 4);
sourcePtr += 4;
pixelLen -= 4;
}
}
destPtr += img->rowPitch;
}
// Transform values
{
auto fdata = reinterpret_cast<float*>(image.GetPixels());
for (size_t j = 0; j < image.GetPixelsSize(); j += 16)
{
int exponent = static_cast<int>(fdata[3]);
fdata[0] = 1.0f / exposure*ldexpf((fdata[0] + 0.5f), exponent - (128 + 8));
fdata[1] = 1.0f / exposure*ldexpf((fdata[1] + 0.5f), exponent - (128 + 8));
fdata[2] = 1.0f / exposure*ldexpf((fdata[2] + 0.5f), exponent - (128 + 8));
fdata[3] = 1.f;
fdata += 4;
}
}
if (metadata)
memcpy(metadata, &mdata, sizeof(TexMetadata));
return S_OK;
}
//-------------------------------------------------------------------------------------
// Load a HDR file from disk
//-------------------------------------------------------------------------------------
_Use_decl_annotations_
HRESULT DirectX::LoadFromHDRFile(const wchar_t* szFile, TexMetadata* metadata, ScratchImage& image)
{
if (!szFile)
return E_INVALIDARG;
image.Release();
#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 for a valid HDR file)
if (fileInfo.EndOfFile.HighPart > 0)
{
return HRESULT_FROM_WIN32(ERROR_FILE_TOO_LARGE);
}
// Need at least enough data to fill the header to be a valid HDR
if (fileInfo.EndOfFile.LowPart < sizeof(g_Signature))
{
return E_FAIL;
}
// Read file
std::unique_ptr<uint8_t[]> temp(new (std::nothrow) uint8_t[fileInfo.EndOfFile.LowPart]);
if (!temp)
{
return E_OUTOFMEMORY;
}
DWORD bytesRead = 0;
if (!ReadFile(hFile.get(), temp.get(), fileInfo.EndOfFile.LowPart, &bytesRead, nullptr))
{
return HRESULT_FROM_WIN32(GetLastError());
}
if (bytesRead != fileInfo.EndOfFile.LowPart)
{
return E_FAIL;
}
return LoadFromHDRMemory(temp.get(), fileInfo.EndOfFile.LowPart, metadata, image);
}
//-------------------------------------------------------------------------------------
// Save a HDR file to memory
//-------------------------------------------------------------------------------------
_Use_decl_annotations_
HRESULT DirectX::SaveToHDRMemory(const Image& image, Blob& blob)
{
if (!image.pixels)
return E_POINTER;
if (image.width > 32767 || image.height > 32767)
{
// Images larger than this can't be RLE encoded. They are technically allowed as
// uncompresssed, but we just don't support them.
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
int bpp;
switch (image.format)
{
case DXGI_FORMAT_R32G32B32A32_FLOAT:
bpp = 4;
break;
case DXGI_FORMAT_R32G32B32_FLOAT:
bpp = 3;
break;
default:
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
blob.Release();
char header[256] = {};
sprintf_s(header, g_Header, image.height, image.width);
auto headerLen = static_cast<DWORD>(strlen(header));
size_t rowPitch = image.width * 4;
size_t slicePitch = image.height * rowPitch;
HRESULT hr = blob.Initialize(headerLen + slicePitch);
if (FAILED(hr))
return hr;
// Copy header
auto dPtr = reinterpret_cast<uint8_t*>(blob.GetBufferPointer());
assert(dPtr != 0);
memcpy_s(dPtr, blob.GetBufferSize(), header, headerLen);
dPtr += headerLen;
#ifdef DISABLE_COMPRESS
// Uncompressed write
auto sPtr = reinterpret_cast<const uint8_t*>(image.pixels);
for (size_t scan = 0; scan < image.height; ++scan)
{
FloatToRGBE(dPtr, reinterpret_cast<const float*>(sPtr), image.width, bpp);
dPtr += rowPitch;
sPtr += image.rowPitch;
}
#else
std::unique_ptr<uint8_t[]> temp(new (std::nothrow) uint8_t[rowPitch * 2]);
if (!temp)
return E_OUTOFMEMORY;
auto rgbe = temp.get();
auto enc = temp.get() + rowPitch;
auto sPtr = reinterpret_cast<const uint8_t*>(image.pixels);
for (size_t scan = 0; scan < image.height; ++scan)
{
FloatToRGBE(rgbe, reinterpret_cast<const float*>(sPtr), image.width, bpp);
sPtr += image.rowPitch;
size_t encSize = EncodeRLE(enc, rgbe, rowPitch, image.width);
if (encSize > 0)
{
memcpy(dPtr, enc, encSize);
dPtr += encSize;
}
else
{
memcpy(dPtr, rgbe, rowPitch);
dPtr += rowPitch;
}
}
#endif
hr = blob.Trim(dPtr - reinterpret_cast<uint8_t*>(blob.GetBufferPointer()));
if (FAILED(hr))
return hr;
return S_OK;
}
//-------------------------------------------------------------------------------------
// Save a HDR file to disk
//-------------------------------------------------------------------------------------
_Use_decl_annotations_
HRESULT DirectX::SaveToHDRFile(const Image& image, const wchar_t* szFile)
{
if (!szFile)
return E_INVALIDARG;
if (!image.pixels)
return E_POINTER;
if (image.width > 32767 || image.height > 32767)
{
// Images larger than this can't be RLE encoded. They are technically allowed as
// uncompresssed, but we just don't support them.
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
int bpp;
switch (image.format)
{
case DXGI_FORMAT_R32G32B32A32_FLOAT:
bpp = 4;
break;
case DXGI_FORMAT_R32G32B32_FLOAT:
bpp = 3;
break;
default:
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
// Create file and write header
#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());
size_t rowPitch = image.width * 4;
size_t slicePitch = image.height * rowPitch;
if (slicePitch < 65535)
{
// For small images, it is better to create an in-memory file and write it out
Blob blob;
HRESULT hr = SaveToHDRMemory(image, blob);
if (FAILED(hr))
return hr;
// Write blob
const DWORD bytesToWrite = static_cast<DWORD>(blob.GetBufferSize());
DWORD bytesWritten;
if (!WriteFile(hFile.get(), blob.GetBufferPointer(), bytesToWrite, &bytesWritten, nullptr))
{
return HRESULT_FROM_WIN32(GetLastError());
}
if (bytesWritten != bytesToWrite)
{
return E_FAIL;
}
}
else
{
// Otherwise, write the image one scanline at a time...
std::unique_ptr<uint8_t[]> temp(new (std::nothrow) uint8_t[rowPitch * 2]);
if (!temp)
return E_OUTOFMEMORY;
auto rgbe = temp.get();
// Write header
char header[256] = {};
sprintf_s(header, g_Header, image.height, image.width);
auto headerLen = static_cast<DWORD>(strlen(header));
DWORD bytesWritten;
if (!WriteFile(hFile.get(), header, headerLen, &bytesWritten, nullptr))
{
return HRESULT_FROM_WIN32(GetLastError());
}
if (bytesWritten != headerLen)
return E_FAIL;
#ifdef DISABLE_COMPRESS
// Uncompressed write
auto sPtr = reinterpret_cast<const uint8_t*>(image.pixels);
for (size_t scan = 0; scan < image.height; ++scan)
{
FloatToRGBE(rgbe, reinterpret_cast<const float*>(sPtr), image.width, bpp);
sPtr += image.rowPitch;
if (!WriteFile(hFile.get(), rgbe, static_cast<DWORD>(rowPitch), &bytesWritten, nullptr))
{
return HRESULT_FROM_WIN32(GetLastError());
}
if (bytesWritten != rowPitch)
return E_FAIL;
}
#else
auto enc = temp.get() + rowPitch;
auto sPtr = reinterpret_cast<const uint8_t*>(image.pixels);
for (size_t scan = 0; scan < image.height; ++scan)
{
FloatToRGBE(rgbe, reinterpret_cast<const float*>(sPtr), image.width, bpp);
sPtr += image.rowPitch;
size_t encSize = EncodeRLE(enc, rgbe, rowPitch, image.width);
if (encSize > 0)
{
if (!WriteFile(hFile.get(), enc, static_cast<DWORD>(encSize), &bytesWritten, nullptr))
{
return HRESULT_FROM_WIN32(GetLastError());
}
if (bytesWritten != encSize)
return E_FAIL;
}
else
{
if (!WriteFile(hFile.get(), rgbe, static_cast<DWORD>(rowPitch), &bytesWritten, nullptr))
{
return HRESULT_FROM_WIN32(GetLastError());
}
if (bytesWritten != rowPitch)
return E_FAIL;
}
}
#endif
}
delonfail.clear();
return S_OK;
}

View File

@ -166,6 +166,41 @@ namespace
return S_OK;
}
//-------------------------------------------------------------------------------------
HRESULT Evaluate_(
const Image& image,
std::function<void __cdecl(_In_reads_(width) const XMVECTOR* pixels, size_t width, size_t y)> pixelFunc)
{
if (!pixelFunc)
return E_INVALIDARG;
if (!image.pixels)
return E_POINTER;
assert(!IsCompressed(image.format));
const size_t width = image.width;
ScopedAlignedArrayXMVECTOR scanline(reinterpret_cast<XMVECTOR*>(_aligned_malloc((sizeof(XMVECTOR)*width), 16)));
if (!scanline)
return E_OUTOFMEMORY;
const uint8_t *pSrc = image.pixels;
const size_t rowPitch = image.rowPitch;
for (size_t h = 0; h < image.height; ++h)
{
if (!_LoadScanline(scanline.get(), width, pSrc, rowPitch, image.format))
return E_FAIL;
pixelFunc(scanline.get(), width, h);
pSrc += rowPitch;
}
return S_OK;
}
};
@ -367,3 +402,40 @@ HRESULT DirectX::ComputeMSE(
}
}
}
//-------------------------------------------------------------------------------------
// Evaluates a user-supplied function for all the pixels in the image
//-------------------------------------------------------------------------------------
_Use_decl_annotations_
HRESULT DirectX::Evaluate(
const Image& image,
std::function<void __cdecl(_In_reads_(width) const XMVECTOR* pixels, size_t width, size_t y)> pixelFunc)
{
if (image.width > UINT32_MAX
|| image.height > UINT32_MAX)
return E_INVALIDARG;
if (IsPlanar(image.format) || IsPalettized(image.format))
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
if (IsCompressed(image.format))
{
ScratchImage temp;
HRESULT hr = Decompress(image, DXGI_FORMAT_R32G32B32A32_FLOAT, temp);
if (FAILED(hr))
return hr;
const Image* img = temp.GetImage(0, 0, 0);
if (!img)
return E_POINTER;
return Evaluate_(*img, pixelFunc);
}
else
{
return Evaluate_(image, pixelFunc);
}
return E_NOTIMPL;
}

View File

@ -1442,3 +1442,19 @@ HRESULT Blob::Initialize(size_t size)
return S_OK;
}
HRESULT Blob::Trim(size_t size)
{
if (!size)
return E_INVALIDARG;
if (!m_buffer)
return E_UNEXPECTED;
if (size > m_size)
return E_INVALIDARG;
m_size = size;
return S_OK;
}

View File

@ -406,6 +406,7 @@
<ClCompile Include="DirectXTexD3D11.cpp" />
<ClCompile Include="DirectXTexDDS.cpp" />
<ClCompile Include="DirectXTexFlipRotate.cpp" />
<ClCompile Include="DirectXTexHDR.cpp" />
<ClCompile Include="DirectXTexImage.cpp" />
<ClCompile Include="DirectXTexMipMaps.cpp" />
<ClCompile Include="DirectXTexMisc.cpp" />

View File

@ -95,6 +95,9 @@
<ClCompile Include="DirectXTexWIC.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DirectXTexHDR.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<CLInclude Include="BC.h">

View File

@ -397,6 +397,7 @@
<ClCompile Include="DirectXTexD3D11.cpp" />
<ClCompile Include="DirectXTexDDS.cpp" />
<ClCompile Include="DirectXTexFlipRotate.cpp" />
<ClCompile Include="DirectXTexHDR.cpp" />
<ClCompile Include="DirectXTexImage.cpp" />
<ClCompile Include="DirectXTexMipMaps.cpp" />
<ClCompile Include="DirectXTexMisc.cpp" />

View File

@ -95,6 +95,9 @@
<ClCompile Include="DirectXTexWIC.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DirectXTexHDR.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<CLInclude Include="BC.h">

View File

@ -406,6 +406,7 @@
<ClCompile Include="DirectXTexD3D11.cpp" />
<ClCompile Include="DirectXTexDDS.cpp" />
<ClCompile Include="DirectXTexFlipRotate.cpp" />
<ClCompile Include="DirectXTexHDR.cpp" />
<ClCompile Include="DirectXTexImage.cpp" />
<ClCompile Include="DirectXTexMipMaps.cpp" />
<ClCompile Include="DirectXTexMisc.cpp" />

View File

@ -95,6 +95,9 @@
<ClCompile Include="DirectXTexWIC.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DirectXTexHDR.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<CLInclude Include="BC.h">

View File

@ -37,6 +37,7 @@
<ClCompile Include="DirectXTexD3D11.cpp" />
<ClCompile Include="DirectXTexDDS.cpp" />
<ClCompile Include="DirectXTexFlipRotate.cpp" />
<ClCompile Include="DirectXTexHDR.cpp" />
<ClCompile Include="DirectXTexImage.cpp" />
<ClCompile Include="DirectXTexMipmaps.cpp" />
<ClCompile Include="DirectXTexMisc.cpp" />

View File

@ -58,6 +58,9 @@
<ClCompile Include="DirectXTexWIC.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DirectXTexHDR.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="DirectXTex.h">

View File

@ -615,6 +615,7 @@
<ClCompile Include="DirectXTexD3D11.cpp" />
<ClCompile Include="DirectXTexDDS.cpp" />
<ClCompile Include="DirectXTexFlipRotate.cpp" />
<ClCompile Include="DirectXTexHDR.cpp" />
<ClCompile Include="DirectXTexImage.cpp" />
<ClCompile Include="DirectXTexMipMaps.cpp" />
<ClCompile Include="DirectXTexMisc.cpp" />

View File

@ -1,10 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns:atg="http://atg.xbox.com" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Resource Files">
<UniqueIdentifier>{8e114980-c1a3-4ada-ad7c-83caadf5daeb}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{c99f7f80-93a7-4692-8567-779ebabe625b}</UniqueIdentifier>
</Filter>
@ -48,6 +44,9 @@
<ClCompile Include="BC4BC5.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DirectXTexHDR.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClCompile Include="BC.cpp">

View File

@ -166,6 +166,7 @@
<ClCompile Include="DirectXTexD3D11.cpp" />
<ClCompile Include="DirectXTexDDS.cpp" />
<ClCompile Include="DirectXTexFlipRotate.cpp" />
<ClCompile Include="DirectXTexHDR.cpp" />
<ClCompile Include="DirectXTexImage.cpp" />
<ClCompile Include="DirectXTexMipMaps.cpp" />
<ClCompile Include="DirectXTexMisc.cpp" />

View File

@ -58,6 +58,9 @@
<ClCompile Include="DirectXTexWIC.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DirectXTexHDR.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<CLInclude Include="DirectXTex.h">

View File

@ -45,6 +45,7 @@
<ClCompile Include="DirectXTexD3D11.cpp" />
<ClCompile Include="DirectXTexDDS.cpp" />
<ClCompile Include="DirectXTexFlipRotate.cpp" />
<ClCompile Include="DirectXTexHDR.cpp" />
<ClCompile Include="DirectXTexImage.cpp" />
<ClCompile Include="DirectXTexMipmaps.cpp" />
<ClCompile Include="DirectXTexMisc.cpp" />

View File

@ -132,5 +132,8 @@
<ClCompile Include="DirectXTexWIC.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DirectXTexHDR.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@ -1,13 +1,22 @@
//--------------------------------------------------------------------------------------
// File: Texassemble.cpp
//
// DirectX 11 Texture assembler for cube maps, volume maps, and arrays
// DirectX Texture assembler for cube maps, volume maps, and arrays
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//--------------------------------------------------------------------------------------
#pragma warning(push)
#pragma warning(disable : 4005)
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#define NODRAWTEXT
#define NOGDI
#define NOBITMAP
#define NOMCX
#define NOSERVICE
#define NOHELP
#pragma warning(pop)
#include <stdio.h>
#include <stdlib.h>
@ -275,7 +284,7 @@ void PrintList(size_t cch, SValue *pValue)
void PrintLogo()
{
wprintf( L"Microsoft (R) DirectX 11 Texture Assembler (DirectXTex version)\n");
wprintf( L"Microsoft (R) DirectX Texture Assembler (DirectXTex version)\n");
wprintf( L"Copyright (C) Microsoft Corp. All rights reserved.\n");
wprintf( L"\n");
}
@ -517,6 +526,15 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
return 1;
}
}
else if ( _wcsicmp( ext, L".hdr" ) == 0 )
{
hr = LoadFromHDRFile( pConv->szSrc, &info, *image.get() );
if ( FAILED(hr) )
{
wprintf( L" FAILED (%x)\n", hr);
return 1;
}
}
else
{
// WIC shares the same filter values for mode and dither

View File

@ -1,13 +1,22 @@
//--------------------------------------------------------------------------------------
// File: TexConv.cpp
//
// DirectX 11 Texture Converter
// DirectX Texture Converter
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//--------------------------------------------------------------------------------------
#pragma warning(push)
#pragma warning(disable : 4005)
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#define NODRAWTEXT
#define NOGDI
#define NOBITMAP
#define NOMCX
#define NOSERVICE
#define NOHELP
#pragma warning(pop)
#include <stdio.h>
#include <stdlib.h>
@ -293,6 +302,7 @@ SValue g_pFilters[] =
#define CODEC_TGA 0xFFFF0002
#define CODEC_HDP 0xFFFF0003
#define CODEC_JXR 0xFFFF0004
#define CODEC_HDR 0xFFFF0005
SValue g_pSaveFileTypes[] = // valid formats to write to
{
@ -302,6 +312,7 @@ SValue g_pSaveFileTypes[] = // valid formats to write to
{ 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 },
@ -451,7 +462,7 @@ void PrintList(size_t cch, SValue *pValue)
void PrintLogo()
{
wprintf( L"Microsoft (R) DirectX 11 Texture Converter (DirectXTex version)\n");
wprintf( L"Microsoft (R) DirectX Texture Converter (DirectXTex version)\n");
wprintf( L"Copyright (C) Microsoft Corp. All rights reserved.\n");
#ifdef _DEBUG
wprintf( L"*** Debug build ***\n");
@ -1204,6 +1215,15 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
continue;
}
}
else if ( _wcsicmp( ext, L".hdr" ) == 0 )
{
hr = LoadFromHDRFile( pConv->szSrc, &info, *image );
if ( FAILED(hr) )
{
wprintf( L" FAILED (%x)\n", hr);
continue;
}
}
else
{
// WIC shares the same filter values for mode and dither
@ -1890,6 +1910,10 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
hr = SaveToTGAFile( img[0], pConv->szDest );
break;
case CODEC_HDR:
hr = SaveToHDRFile( img[0], pConv->szDest );
break;
default:
{
WICCodecs codec = (FileType == CODEC_HDP || FileType == CODEC_JXR) ? WIC_CODEC_WMP : static_cast<WICCodecs>(FileType);