mirror of
https://github.com/microsoft/DirectXTex.git
synced 2026-02-09 22:26:13 +01:00
Update TGA codec for TGA 2.0 (#151)
This commit is contained in:
@@ -382,8 +382,8 @@ namespace DirectX
|
||||
_In_z_ const wchar_t* szFile,
|
||||
_Out_opt_ TexMetadata* metadata, _Out_ ScratchImage& image);
|
||||
|
||||
HRESULT __cdecl SaveToTGAMemory(_In_ const Image& image, _Out_ Blob& blob);
|
||||
HRESULT __cdecl SaveToTGAFile(_In_ const Image& image, _In_z_ const wchar_t* szFile);
|
||||
HRESULT __cdecl SaveToTGAMemory(_In_ const Image& image, _Out_ Blob& blob, _In_opt_ const TexMetadata* metadata = nullptr);
|
||||
HRESULT __cdecl SaveToTGAFile(_In_ const Image& image, _In_z_ const wchar_t* szFile, _In_opt_ const TexMetadata* metadata = nullptr);
|
||||
|
||||
// WIC operations
|
||||
HRESULT __cdecl LoadFromWICMemory(
|
||||
|
||||
@@ -110,6 +110,7 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
#include <search.h>
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@ using namespace DirectX;
|
||||
|
||||
namespace
|
||||
{
|
||||
const char g_Signature[] = "TRUEVISION-XFILE.";
|
||||
// This is the official footer signature for the TGA 2.0 file format.
|
||||
|
||||
enum TGAImageType
|
||||
{
|
||||
TGA_NO_IMAGE = 0,
|
||||
@@ -42,6 +45,15 @@ namespace
|
||||
TGA_FLAGS_INTERLEAVED_4WAY = 0x80, // Deprecated
|
||||
};
|
||||
|
||||
enum TGAAttributesType : uint8_t
|
||||
{
|
||||
TGA_ATTRIBUTE_NONE = 0, // 0: no alpha data included
|
||||
TGA_ATTRIBUTE_IGNORED = 1, // 1: undefined data, can be ignored
|
||||
TGA_ATTRIBUTE_UNDEFINED = 2, // 2: uedefined data, should be retained
|
||||
TGA_ATTRIBUTE_ALPHA = 3, // 3: useful alpha channel data
|
||||
TGA_ATTRIBUTE_PREMULTIPLIED = 4, // 4: pre-multiplied alpha
|
||||
};
|
||||
|
||||
#pragma pack(push,1)
|
||||
struct TGA_HEADER
|
||||
{
|
||||
@@ -892,6 +904,82 @@ namespace
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// TGA 2.0 Extension helpers
|
||||
//-------------------------------------------------------------------------------------
|
||||
void SetExtension(TGA_EXTENSION *ext, const TexMetadata& metadata)
|
||||
{
|
||||
memset(ext, 0, sizeof(TGA_EXTENSION));
|
||||
|
||||
ext->wSize = sizeof(TGA_EXTENSION);
|
||||
|
||||
memcpy(ext->szSoftwareId, "DirectXTex", sizeof("DirectXTex"));
|
||||
ext->wVersionNumber = DIRECTX_TEX_VERSION;
|
||||
ext->bVersionLetter = ' ';
|
||||
|
||||
if (IsSRGB(metadata.format))
|
||||
{
|
||||
ext->wGammaNumerator = 22;
|
||||
ext->wGammaDenominator = 10;
|
||||
}
|
||||
|
||||
switch (metadata.GetAlphaMode())
|
||||
{
|
||||
case TEX_ALPHA_MODE_UNKNOWN:
|
||||
ext->bAttributesType = HasAlpha(metadata.format) ? TGA_ATTRIBUTE_UNDEFINED : TGA_ATTRIBUTE_NONE;
|
||||
break;
|
||||
|
||||
case TEX_ALPHA_MODE_STRAIGHT:
|
||||
ext->bAttributesType = TGA_ATTRIBUTE_ALPHA;
|
||||
break;
|
||||
|
||||
case TEX_ALPHA_MODE_PREMULTIPLIED:
|
||||
ext->bAttributesType = TGA_ATTRIBUTE_PREMULTIPLIED;
|
||||
break;
|
||||
|
||||
case TEX_ALPHA_MODE_OPAQUE:
|
||||
ext->bAttributesType = TGA_ATTRIBUTE_IGNORED;
|
||||
break;
|
||||
|
||||
case TEX_ALPHA_MODE_CUSTOM:
|
||||
ext->bAttributesType = TGA_ATTRIBUTE_UNDEFINED;
|
||||
break;
|
||||
}
|
||||
|
||||
// Set file time stamp
|
||||
{
|
||||
time_t now = {};
|
||||
time(&now);
|
||||
|
||||
tm info;
|
||||
if (!gmtime_s(&info, &now))
|
||||
{
|
||||
ext->wStampMonth = static_cast<uint16_t>(info.tm_mon + 1);
|
||||
ext->wStampDay = static_cast<uint16_t>(info.tm_mday);
|
||||
ext->wStampYear = static_cast<uint16_t>(info.tm_year + 1900);
|
||||
ext->wStampHour = static_cast<uint16_t>(info.tm_hour);
|
||||
ext->wStampMinute = static_cast<uint16_t>(info.tm_min);
|
||||
ext->wStampSecond = static_cast<uint16_t>(info.tm_sec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEX_ALPHA_MODE GetAlphaModeFromExtension(const TGA_EXTENSION *ext)
|
||||
{
|
||||
if (ext && ext->wSize == sizeof(TGA_EXTENSION))
|
||||
{
|
||||
switch (ext->bAttributesType)
|
||||
{
|
||||
case TGA_ATTRIBUTE_IGNORED: return TEX_ALPHA_MODE_OPAQUE;
|
||||
case TGA_ATTRIBUTE_UNDEFINED: return TEX_ALPHA_MODE_CUSTOM;
|
||||
case TGA_ATTRIBUTE_ALPHA: return TEX_ALPHA_MODE_STRAIGHT;
|
||||
case TGA_ATTRIBUTE_PREMULTIPLIED: return TEX_ALPHA_MODE_PREMULTIPLIED;
|
||||
}
|
||||
}
|
||||
|
||||
return TEX_ALPHA_MODE_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1021,6 +1109,21 @@ HRESULT DirectX::LoadFromTGAMemory(
|
||||
{
|
||||
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));
|
||||
|
||||
if (memcmp(footer->Signature, g_Signature, sizeof(g_Signature)) == 0)
|
||||
{
|
||||
if (footer->dwExtensionOffset != 0
|
||||
&& ((footer->dwExtensionOffset + sizeof(TGA_EXTENSION)) <= size))
|
||||
{
|
||||
auto ext = reinterpret_cast<const TGA_EXTENSION*>(static_cast<const uint8_t*>(pSource) + footer->dwExtensionOffset);
|
||||
metadata->SetAlphaMode(GetAlphaModeFromExtension(ext));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
@@ -1312,6 +1415,46 @@ HRESULT DirectX::LoadFromTGAFile(
|
||||
{
|
||||
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)), 0, FILE_END) == INVALID_SET_FILE_POINTER)
|
||||
{
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
|
||||
if (!ReadFile(hFile.get(), &footer, sizeof(TGA_FOOTER), &bytesRead, nullptr))
|
||||
{
|
||||
image.Release();
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
|
||||
if (bytesRead != sizeof(TGA_FOOTER))
|
||||
{
|
||||
image.Release();
|
||||
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))
|
||||
{
|
||||
TGA_EXTENSION ext = {};
|
||||
if (ReadFile(hFile.get(), &ext, sizeof(TGA_EXTENSION), &bytesRead, nullptr)
|
||||
&& bytesRead == sizeof(TGA_EXTENSION))
|
||||
{
|
||||
metadata->SetAlphaMode(GetAlphaModeFromExtension(&ext));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
@@ -1322,7 +1465,7 @@ HRESULT DirectX::LoadFromTGAFile(
|
||||
// Save a TGA file to memory
|
||||
//-------------------------------------------------------------------------------------
|
||||
_Use_decl_annotations_
|
||||
HRESULT DirectX::SaveToTGAMemory(const Image& image, Blob& blob)
|
||||
HRESULT DirectX::SaveToTGAMemory(const Image& image, Blob& blob, const TexMetadata* metadata)
|
||||
{
|
||||
if (!image.pixels)
|
||||
return E_POINTER;
|
||||
@@ -1349,13 +1492,18 @@ HRESULT DirectX::SaveToTGAMemory(const Image& image, Blob& blob)
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = blob.Initialize(sizeof(TGA_HEADER) + slicePitch);
|
||||
hr = blob.Initialize(sizeof(TGA_HEADER)
|
||||
+ slicePitch
|
||||
+ (metadata ? sizeof(TGA_EXTENSION) : 0)
|
||||
+ sizeof(TGA_FOOTER));
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
// Copy header
|
||||
auto dPtr = static_cast<uint8_t*>(blob.GetBufferPointer());
|
||||
assert(dPtr != nullptr);
|
||||
auto destPtr = static_cast<uint8_t*>(blob.GetBufferPointer());
|
||||
assert(destPtr != nullptr);
|
||||
|
||||
uint8_t* dPtr = destPtr;
|
||||
memcpy_s(dPtr, blob.GetBufferSize(), &tga_header, sizeof(TGA_HEADER));
|
||||
dPtr += sizeof(TGA_HEADER);
|
||||
|
||||
@@ -1382,6 +1530,24 @@ HRESULT DirectX::SaveToTGAMemory(const Image& image, Blob& blob)
|
||||
pPixels += image.rowPitch;
|
||||
}
|
||||
|
||||
uint32_t extOffset = 0;
|
||||
if (metadata)
|
||||
{
|
||||
// metadata is only used for writing the TGA 2.0 extension header
|
||||
|
||||
auto ext = reinterpret_cast<TGA_EXTENSION*>(dPtr);
|
||||
SetExtension(ext, *metadata);
|
||||
|
||||
extOffset = static_cast<uint32_t>(dPtr - destPtr);
|
||||
dPtr += sizeof(TGA_EXTENSION);
|
||||
}
|
||||
|
||||
// Copy TGA 2.0 footer
|
||||
auto footer = reinterpret_cast<TGA_FOOTER*>(dPtr);
|
||||
footer->dwDeveloperOffset = 0;
|
||||
footer->dwExtensionOffset = extOffset;
|
||||
memcpy(footer->Signature, g_Signature, sizeof(g_Signature));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@@ -1390,7 +1556,7 @@ HRESULT DirectX::SaveToTGAMemory(const Image& image, Blob& blob)
|
||||
// Save a TGA file to disk
|
||||
//-------------------------------------------------------------------------------------
|
||||
_Use_decl_annotations_
|
||||
HRESULT DirectX::SaveToTGAFile(const Image& image, const wchar_t* szFile)
|
||||
HRESULT DirectX::SaveToTGAFile(const Image& image, const wchar_t* szFile, const TexMetadata* metadata)
|
||||
{
|
||||
if (!szFile)
|
||||
return E_INVALIDARG;
|
||||
@@ -1513,6 +1679,41 @@ HRESULT DirectX::SaveToTGAFile(const Image& image, const wchar_t* szFile)
|
||||
if (bytesWritten != rowPitch)
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
uint32_t extOffset = 0;
|
||||
if (metadata)
|
||||
{
|
||||
// metadata is only used for writing the TGA 2.0 extension header
|
||||
TGA_EXTENSION ext = {};
|
||||
SetExtension(&ext, *metadata);
|
||||
|
||||
extOffset = SetFilePointer(hFile.get(), 0, nullptr, FILE_CURRENT);
|
||||
if (extOffset == INVALID_SET_FILE_POINTER)
|
||||
{
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
|
||||
if (!WriteFile(hFile.get(), &ext, sizeof(TGA_EXTENSION), &bytesWritten, nullptr))
|
||||
{
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
|
||||
if (bytesWritten != sizeof(TGA_EXTENSION))
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
// Write TGA 2.0 footer
|
||||
TGA_FOOTER footer = {};
|
||||
footer.dwExtensionOffset = extOffset;
|
||||
memcpy(footer.Signature, g_Signature, sizeof(g_Signature));
|
||||
|
||||
if (!WriteFile(hFile.get(), &footer, sizeof(footer), &bytesWritten, nullptr))
|
||||
{
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
|
||||
if (bytesWritten != sizeof(footer))
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
delonfail.clear();
|
||||
|
||||
Reference in New Issue
Block a user