mirror of
https://github.com/microsoft/DirectXTex.git
synced 2025-07-15 22:40:14 +02:00
TGA flags for sRGB colorspace in TGA 2.0 extension area (#199)
This commit is contained in:
parent
5624f948af
commit
836cabd0f9
@ -201,6 +201,18 @@ namespace DirectX
|
|||||||
|
|
||||||
TGA_FLAGS_ALLOW_ALL_ZERO_ALPHA = 0x2,
|
TGA_FLAGS_ALLOW_ALL_ZERO_ALPHA = 0x2,
|
||||||
// If the loaded image has an all zero alpha channel, normally we assume it should be opaque. This flag leaves it alone.
|
// If the loaded image has an all zero alpha channel, normally we assume it should be opaque. This flag leaves it alone.
|
||||||
|
|
||||||
|
TGA_FLAGS_IGNORE_SRGB = 0x10,
|
||||||
|
// Ignores sRGB TGA 2.0 metadata if present in the file
|
||||||
|
|
||||||
|
TGA_FLAGS_FORCE_SRGB = 0x20,
|
||||||
|
// Writes sRGB metadata into the file reguardless of format (TGA 2.0 only)
|
||||||
|
|
||||||
|
TGA_FLAGS_FORCE_LINEAR = 0x40,
|
||||||
|
// Writes linear gamma metadata into the file reguardless of format (TGA 2.0 only)
|
||||||
|
|
||||||
|
TGA_FLAGS_DEFAULT_SRGB = 0x80,
|
||||||
|
// If no colorspace is specified in TGA 2.0 metadata, assume sRGB
|
||||||
};
|
};
|
||||||
|
|
||||||
enum WIC_FLAGS : unsigned long
|
enum WIC_FLAGS : unsigned long
|
||||||
|
@ -23,6 +23,8 @@ using namespace DirectX;
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
constexpr float GAMMA_EPSILON = 0.01f;
|
||||||
|
|
||||||
const char g_Signature[] = "TRUEVISION-XFILE.";
|
const char g_Signature[] = "TRUEVISION-XFILE.";
|
||||||
// This is the official footer signature for the TGA 2.0 file format.
|
// This is the official footer signature for the TGA 2.0 file format.
|
||||||
|
|
||||||
@ -1171,7 +1173,7 @@ namespace
|
|||||||
//-------------------------------------------------------------------------------------
|
//-------------------------------------------------------------------------------------
|
||||||
// TGA 2.0 Extension helpers
|
// TGA 2.0 Extension helpers
|
||||||
//-------------------------------------------------------------------------------------
|
//-------------------------------------------------------------------------------------
|
||||||
void SetExtension(TGA_EXTENSION *ext, const TexMetadata& metadata) noexcept
|
void SetExtension(_In_ TGA_EXTENSION *ext, TGA_FLAGS flags, const TexMetadata& metadata) noexcept
|
||||||
{
|
{
|
||||||
memset(ext, 0, sizeof(TGA_EXTENSION));
|
memset(ext, 0, sizeof(TGA_EXTENSION));
|
||||||
|
|
||||||
@ -1181,11 +1183,17 @@ namespace
|
|||||||
ext->wVersionNumber = DIRECTX_TEX_VERSION;
|
ext->wVersionNumber = DIRECTX_TEX_VERSION;
|
||||||
ext->bVersionLetter = ' ';
|
ext->bVersionLetter = ' ';
|
||||||
|
|
||||||
if (IsSRGB(metadata.format))
|
bool sRGB = ((flags & TGA_FLAGS_FORCE_LINEAR) == 0) && ((flags & TGA_FLAGS_FORCE_SRGB) != 0 || IsSRGB(metadata.format));
|
||||||
|
if (sRGB)
|
||||||
{
|
{
|
||||||
ext->wGammaNumerator = 22;
|
ext->wGammaNumerator = 22;
|
||||||
ext->wGammaDenominator = 10;
|
ext->wGammaDenominator = 10;
|
||||||
}
|
}
|
||||||
|
else if (flags & TGA_FLAGS_FORCE_LINEAR)
|
||||||
|
{
|
||||||
|
ext->wGammaNumerator = 1;
|
||||||
|
ext->wGammaDenominator = 1;
|
||||||
|
}
|
||||||
|
|
||||||
switch (metadata.GetAlphaMode())
|
switch (metadata.GetAlphaMode())
|
||||||
{
|
{
|
||||||
@ -1228,7 +1236,7 @@ namespace
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEX_ALPHA_MODE GetAlphaModeFromExtension(const TGA_EXTENSION *ext) noexcept
|
TEX_ALPHA_MODE GetAlphaModeFromExtension(_In_opt_ const TGA_EXTENSION *ext) noexcept
|
||||||
{
|
{
|
||||||
if (ext && ext->wSize == sizeof(TGA_EXTENSION))
|
if (ext && ext->wSize == sizeof(TGA_EXTENSION))
|
||||||
{
|
{
|
||||||
@ -1243,6 +1251,35 @@ namespace
|
|||||||
|
|
||||||
return TEX_ALPHA_MODE_UNKNOWN;
|
return TEX_ALPHA_MODE_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DXGI_FORMAT GetSRGBFromExtension(_In_opt_ const TGA_EXTENSION* ext, DXGI_FORMAT format, TGA_FLAGS flags, _In_opt_ ScratchImage* image) noexcept
|
||||||
|
{
|
||||||
|
bool sRGB = false;
|
||||||
|
|
||||||
|
if (ext && ext->wSize == sizeof(TGA_EXTENSION) && ext->wGammaDenominator != 0)
|
||||||
|
{
|
||||||
|
float gamma = static_cast<float>(ext->wGammaNumerator) / static_cast<float>(ext->wGammaDenominator);
|
||||||
|
if (fabsf(gamma - 2.2f) < GAMMA_EPSILON || fabsf(gamma - 2.4f) < GAMMA_EPSILON)
|
||||||
|
{
|
||||||
|
sRGB = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sRGB = (flags & TGA_FLAGS_DEFAULT_SRGB) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sRGB)
|
||||||
|
{
|
||||||
|
format = MakeSRGB(format);
|
||||||
|
if (image)
|
||||||
|
{
|
||||||
|
image->OverrideFormat(format);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return format;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1264,7 +1301,33 @@ HRESULT DirectX::GetMetadataFromTGAMemory(
|
|||||||
return E_INVALIDARG;
|
return E_INVALIDARG;
|
||||||
|
|
||||||
size_t offset;
|
size_t offset;
|
||||||
return DecodeTGAHeader(pSource, size, flags, metadata, offset, nullptr);
|
HRESULT hr = DecodeTGAHeader(pSource, size, flags, metadata, offset, nullptr);
|
||||||
|
if (FAILED(hr))
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
// Optional TGA 2.0 footer & extension area
|
||||||
|
const TGA_EXTENSION* ext = nullptr;
|
||||||
|
if (size >= sizeof(TGA_FOOTER))
|
||||||
|
{
|
||||||
|
auto footer = reinterpret_cast<const TGA_FOOTER*>(static_cast<const uint8_t*>(pSource) + size - sizeof(TGA_FOOTER));
|
||||||
|
|
||||||
|
if (memcmp(footer->Signature, g_Signature, sizeof(g_Signature)) == 0)
|
||||||
|
{
|
||||||
|
if (footer->dwExtensionOffset != 0
|
||||||
|
&& ((footer->dwExtensionOffset + sizeof(TGA_EXTENSION)) <= size))
|
||||||
|
{
|
||||||
|
ext = reinterpret_cast<const TGA_EXTENSION*>(static_cast<const uint8_t*>(pSource) + footer->dwExtensionOffset);
|
||||||
|
metadata.SetAlphaMode(GetAlphaModeFromExtension(ext));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(flags & TGA_FLAGS_IGNORE_SRGB))
|
||||||
|
{
|
||||||
|
metadata.format = GetSRGBFromExtension(ext, metadata.format, flags, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
_Use_decl_annotations_
|
_Use_decl_annotations_
|
||||||
@ -1312,7 +1375,55 @@ HRESULT DirectX::GetMetadataFromTGAFile(const wchar_t* szFile, TGA_FLAGS flags,
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t offset;
|
size_t offset;
|
||||||
return DecodeTGAHeader(header, bytesRead, flags, metadata, offset, nullptr);
|
HRESULT hr = DecodeTGAHeader(header, bytesRead, flags, metadata, offset, nullptr);
|
||||||
|
if (FAILED(hr))
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
// Optional TGA 2.0 footer & extension area
|
||||||
|
const TGA_EXTENSION* ext = nullptr;
|
||||||
|
TGA_EXTENSION extData = {};
|
||||||
|
{
|
||||||
|
if (SetFilePointer(hFile.get(), -static_cast<int>(sizeof(TGA_FOOTER)), nullptr, FILE_END) == INVALID_SET_FILE_POINTER)
|
||||||
|
{
|
||||||
|
return HRESULT_FROM_WIN32(GetLastError());
|
||||||
|
}
|
||||||
|
|
||||||
|
TGA_FOOTER footer = {};
|
||||||
|
if (!ReadFile(hFile.get(), &footer, sizeof(TGA_FOOTER), &bytesRead, nullptr))
|
||||||
|
{
|
||||||
|
return HRESULT_FROM_WIN32(GetLastError());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bytesRead != sizeof(TGA_FOOTER))
|
||||||
|
{
|
||||||
|
return E_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memcmp(footer.Signature, g_Signature, sizeof(g_Signature)) == 0)
|
||||||
|
{
|
||||||
|
if (footer.dwExtensionOffset != 0
|
||||||
|
&& ((footer.dwExtensionOffset + sizeof(TGA_EXTENSION)) <= fileInfo.EndOfFile.LowPart))
|
||||||
|
{
|
||||||
|
LARGE_INTEGER filePos = { { static_cast<DWORD>(footer.dwExtensionOffset), 0 } };
|
||||||
|
if (SetFilePointerEx(hFile.get(), filePos, nullptr, FILE_BEGIN))
|
||||||
|
{
|
||||||
|
if (ReadFile(hFile.get(), &extData, sizeof(TGA_EXTENSION), &bytesRead, nullptr)
|
||||||
|
&& bytesRead == sizeof(TGA_EXTENSION))
|
||||||
|
{
|
||||||
|
ext = &extData;
|
||||||
|
metadata.SetAlphaMode(GetAlphaModeFromExtension(ext));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(flags & TGA_FLAGS_IGNORE_SRGB))
|
||||||
|
{
|
||||||
|
metadata.format = GetSRGBFromExtension(ext, metadata.format, flags, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1367,16 +1478,10 @@ HRESULT DirectX::LoadFromTGAMemory(
|
|||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (metadata)
|
// Optional TGA 2.0 footer & extension area
|
||||||
|
const TGA_EXTENSION* ext = nullptr;
|
||||||
|
if (size >= sizeof(TGA_FOOTER))
|
||||||
{
|
{
|
||||||
memcpy(metadata, &mdata, sizeof(TexMetadata));
|
|
||||||
if (hr == S_FALSE)
|
|
||||||
{
|
|
||||||
metadata->SetAlphaMode(TEX_ALPHA_MODE_OPAQUE);
|
|
||||||
}
|
|
||||||
else if (size >= sizeof(TGA_FOOTER))
|
|
||||||
{
|
|
||||||
// Handle optional TGA 2.0 footer
|
|
||||||
auto footer = reinterpret_cast<const TGA_FOOTER*>(static_cast<const uint8_t*>(pSource) + size - sizeof(TGA_FOOTER));
|
auto footer = reinterpret_cast<const TGA_FOOTER*>(static_cast<const uint8_t*>(pSource) + size - sizeof(TGA_FOOTER));
|
||||||
|
|
||||||
if (memcmp(footer->Signature, g_Signature, sizeof(g_Signature)) == 0)
|
if (memcmp(footer->Signature, g_Signature, sizeof(g_Signature)) == 0)
|
||||||
@ -1384,12 +1489,28 @@ HRESULT DirectX::LoadFromTGAMemory(
|
|||||||
if (footer->dwExtensionOffset != 0
|
if (footer->dwExtensionOffset != 0
|
||||||
&& ((footer->dwExtensionOffset + sizeof(TGA_EXTENSION)) <= size))
|
&& ((footer->dwExtensionOffset + sizeof(TGA_EXTENSION)) <= size))
|
||||||
{
|
{
|
||||||
auto ext = reinterpret_cast<const TGA_EXTENSION*>(static_cast<const uint8_t*>(pSource) + footer->dwExtensionOffset);
|
ext = reinterpret_cast<const TGA_EXTENSION*>(static_cast<const uint8_t*>(pSource) + footer->dwExtensionOffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(flags & TGA_FLAGS_IGNORE_SRGB))
|
||||||
|
{
|
||||||
|
mdata.format = GetSRGBFromExtension(ext, mdata.format, flags, &image);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (metadata)
|
||||||
|
{
|
||||||
|
memcpy(metadata, &mdata, sizeof(TexMetadata));
|
||||||
|
if (hr == S_FALSE)
|
||||||
|
{
|
||||||
|
metadata->SetAlphaMode(TEX_ALPHA_MODE_OPAQUE);
|
||||||
|
}
|
||||||
|
else if (ext)
|
||||||
|
{
|
||||||
metadata->SetAlphaMode(GetAlphaModeFromExtension(ext));
|
metadata->SetAlphaMode(GetAlphaModeFromExtension(ext));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
@ -1734,23 +1855,16 @@ HRESULT DirectX::LoadFromTGAFile(
|
|||||||
opaquealpha = true;
|
opaquealpha = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (metadata)
|
// Optional TGA 2.0 footer & extension area
|
||||||
|
const TGA_EXTENSION* ext = nullptr;
|
||||||
|
TGA_EXTENSION extData = {};
|
||||||
{
|
{
|
||||||
memcpy(metadata, &mdata, sizeof(TexMetadata));
|
|
||||||
if (opaquealpha)
|
|
||||||
{
|
|
||||||
metadata->SetAlphaMode(TEX_ALPHA_MODE_OPAQUE);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Handle optional TGA 2.0 footer
|
|
||||||
TGA_FOOTER footer = {};
|
|
||||||
|
|
||||||
if (SetFilePointer(hFile.get(), -static_cast<int>(sizeof(TGA_FOOTER)), nullptr, FILE_END) == INVALID_SET_FILE_POINTER)
|
if (SetFilePointer(hFile.get(), -static_cast<int>(sizeof(TGA_FOOTER)), nullptr, FILE_END) == INVALID_SET_FILE_POINTER)
|
||||||
{
|
{
|
||||||
return HRESULT_FROM_WIN32(GetLastError());
|
return HRESULT_FROM_WIN32(GetLastError());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TGA_FOOTER footer = {};
|
||||||
if (!ReadFile(hFile.get(), &footer, sizeof(TGA_FOOTER), &bytesRead, nullptr))
|
if (!ReadFile(hFile.get(), &footer, sizeof(TGA_FOOTER), &bytesRead, nullptr))
|
||||||
{
|
{
|
||||||
image.Release();
|
image.Release();
|
||||||
@ -1771,16 +1885,32 @@ HRESULT DirectX::LoadFromTGAFile(
|
|||||||
LARGE_INTEGER filePos = { { static_cast<DWORD>(footer.dwExtensionOffset), 0 } };
|
LARGE_INTEGER filePos = { { static_cast<DWORD>(footer.dwExtensionOffset), 0 } };
|
||||||
if (SetFilePointerEx(hFile.get(), filePos, nullptr, FILE_BEGIN))
|
if (SetFilePointerEx(hFile.get(), filePos, nullptr, FILE_BEGIN))
|
||||||
{
|
{
|
||||||
TGA_EXTENSION ext = {};
|
if (ReadFile(hFile.get(), &extData, sizeof(TGA_EXTENSION), &bytesRead, nullptr)
|
||||||
if (ReadFile(hFile.get(), &ext, sizeof(TGA_EXTENSION), &bytesRead, nullptr)
|
|
||||||
&& bytesRead == sizeof(TGA_EXTENSION))
|
&& bytesRead == sizeof(TGA_EXTENSION))
|
||||||
{
|
{
|
||||||
metadata->SetAlphaMode(GetAlphaModeFromExtension(&ext));
|
ext = &extData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!(flags & TGA_FLAGS_IGNORE_SRGB))
|
||||||
|
{
|
||||||
|
mdata.format = GetSRGBFromExtension(ext, mdata.format, flags, &image);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (metadata)
|
||||||
|
{
|
||||||
|
memcpy(metadata, &mdata, sizeof(TexMetadata));
|
||||||
|
if (opaquealpha)
|
||||||
|
{
|
||||||
|
metadata->SetAlphaMode(TEX_ALPHA_MODE_OPAQUE);
|
||||||
|
}
|
||||||
|
else if (ext)
|
||||||
|
{
|
||||||
|
metadata->SetAlphaMode(GetAlphaModeFromExtension(ext));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
@ -1793,10 +1923,13 @@ HRESULT DirectX::LoadFromTGAFile(
|
|||||||
_Use_decl_annotations_
|
_Use_decl_annotations_
|
||||||
HRESULT DirectX::SaveToTGAMemory(
|
HRESULT DirectX::SaveToTGAMemory(
|
||||||
const Image& image,
|
const Image& image,
|
||||||
TGA_FLAGS /*flags*/,
|
TGA_FLAGS flags,
|
||||||
Blob& blob,
|
Blob& blob,
|
||||||
const TexMetadata* metadata) noexcept
|
const TexMetadata* metadata) noexcept
|
||||||
{
|
{
|
||||||
|
if ((flags & (TGA_FLAGS_FORCE_LINEAR | TGA_FLAGS_FORCE_SRGB)) != 0 && !metadata)
|
||||||
|
return E_INVALIDARG;
|
||||||
|
|
||||||
if (!image.pixels)
|
if (!image.pixels)
|
||||||
return E_POINTER;
|
return E_POINTER;
|
||||||
|
|
||||||
@ -1857,9 +1990,8 @@ HRESULT DirectX::SaveToTGAMemory(
|
|||||||
if (metadata)
|
if (metadata)
|
||||||
{
|
{
|
||||||
// metadata is only used for writing the TGA 2.0 extension header
|
// metadata is only used for writing the TGA 2.0 extension header
|
||||||
|
|
||||||
auto ext = reinterpret_cast<TGA_EXTENSION*>(dPtr);
|
auto ext = reinterpret_cast<TGA_EXTENSION*>(dPtr);
|
||||||
SetExtension(ext, *metadata);
|
SetExtension(ext, flags, *metadata);
|
||||||
|
|
||||||
extOffset = static_cast<uint32_t>(dPtr - destPtr);
|
extOffset = static_cast<uint32_t>(dPtr - destPtr);
|
||||||
dPtr += sizeof(TGA_EXTENSION);
|
dPtr += sizeof(TGA_EXTENSION);
|
||||||
@ -1888,6 +2020,9 @@ HRESULT DirectX::SaveToTGAFile(
|
|||||||
if (!szFile)
|
if (!szFile)
|
||||||
return E_INVALIDARG;
|
return E_INVALIDARG;
|
||||||
|
|
||||||
|
if ((flags & (TGA_FLAGS_FORCE_LINEAR | TGA_FLAGS_FORCE_SRGB)) != 0 && !metadata)
|
||||||
|
return E_INVALIDARG;
|
||||||
|
|
||||||
if (!image.pixels)
|
if (!image.pixels)
|
||||||
return E_POINTER;
|
return E_POINTER;
|
||||||
|
|
||||||
@ -1994,7 +2129,7 @@ HRESULT DirectX::SaveToTGAFile(
|
|||||||
{
|
{
|
||||||
// metadata is only used for writing the TGA 2.0 extension header
|
// metadata is only used for writing the TGA 2.0 extension header
|
||||||
TGA_EXTENSION ext = {};
|
TGA_EXTENSION ext = {};
|
||||||
SetExtension(&ext, *metadata);
|
SetExtension(&ext, flags, *metadata);
|
||||||
|
|
||||||
extOffset = SetFilePointer(hFile.get(), 0, nullptr, FILE_CURRENT);
|
extOffset = SetFilePointer(hFile.get(), 0, nullptr, FILE_CURRENT);
|
||||||
if (extOffset == INVALID_SET_FILE_POINTER)
|
if (extOffset == INVALID_SET_FILE_POINTER)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user